This commit is contained in:
2026-07-12 00:26:17 +08:00
parent b080747b56
commit 7d90624a8a
8 changed files with 2819 additions and 15 deletions
@@ -113,8 +113,22 @@ namespace Assets.MyGame.Scripts.Net
break;
case "ReadyNotice":
//roomMainPanel.subUIManager.CloseAll();
roomMainPanel.InitRoomData();
{
// ReadyNotice arguments[0] 是用户ID字符串,不是房间对象
var userId = JsonConvert.DeserializeObject<WebSocketResponseBase<string>>(msg).arguments[0];
// 标记该玩家已准备
if (roomMainPanel.roomData != null && roomMainPanel.roomData.users != null)
{
var u = roomMainPanel.roomData.users.Find(x => x.userId == userId);
if (u != null) u.isReady = true;
// 只有双方都准备好才进入游戏
if (roomMainPanel.roomData.users.Count >= 2
&& roomMainPanel.roomData.users.TrueForAll(x => x.isReady))
{
roomMainPanel.InitRoomData();
}
}
}
break;
case "GameOperation":
{
@@ -156,6 +156,13 @@ namespace Assets.MyGame.Scripts.Net
}
public static void RoomKillNotice(string msg)
{
// 离开倒计时期间不触发击杀/爆奖特效
var panel = UIManager.Instance.Get<GameMainPanel_FishBase>(UIPanel.MainGamePanelDefault)
?? UIManager.Instance.Get<GameMainPanel_FishBase>(UIPanel.MainGamePanelFLZY)
?? UIManager.Instance.Get<GameMainPanel_FishBase>(UIPanel.MainGamePanelYYSC)
?? UIManager.Instance.Get<GameMainPanel_FishBase>(UIPanel.MainGamePanelXJMH)
?? UIManager.Instance.Get<GameMainPanel_FishBase>(UIPanel.MainGamePanelHHWK);
if (panel != null && panel.IsLeavingCountdown()) return;
CreateFishManager.Instance.Ws_DieFish(msg);
}
public static void RoomFireNotice(string msg)
@@ -1,5 +1,77 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!1 &428869252117790975
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 1755596749695405114}
- component: {fileID: 2500953553794549456}
- component: {fileID: 6633565018604319386}
m_Layer: 0
m_Name: Renderer0
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!224 &1755596749695405114
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 428869252117790975}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 6605999270105133777}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0.5, y: 0.5}
m_AnchorMax: {x: 0.5, y: 0.5}
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 100, y: 100}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!222 &2500953553794549456
CanvasRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 428869252117790975}
m_CullTransparentMesh: 1
--- !u!114 &6633565018604319386
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 428869252117790975}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 1344c3c82d62a2a41a3576d8abb8e3ea, type: 3}
m_Name:
m_EditorClassIdentifier:
m_Material: {fileID: 0}
m_Color: {r: 1, g: 1, b: 1, a: 1}
m_RaycastTarget: 0
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
m_Maskable: 1
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_Texture: {fileID: 0}
m_UVRect:
serializedVersion: 2
x: 0
y: 0
width: 1
height: 1
--- !u!1 &451279130178473817
GameObject:
m_ObjectHideFlags: 0
@@ -599,6 +671,7 @@ RectTransform:
- {fileID: 2724128015881410653}
- {fileID: 551837965639396177}
- {fileID: 4852173693812700946}
- {fileID: 6605999270105133777}
m_Father: {fileID: 0}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 0}
@@ -675,6 +748,107 @@ MonoBehaviour:
tmp_CoinCount_Top: {fileID: 5805709281203068529}
tmp_WaitTime_Top: {fileID: 7342989625873532498}
img_Title_Top: {fileID: 5212885171862005544}
skeletonGraphic: {fileID: 2515020745373521213}
skineName_JinXingZhong: jrz
skineName_KaiShi: ks
skineName_PiPeiChengGong: ppcg
skineName_ZhengZaiPiPei: zzpp
animName_Close: YXN_pipei_close
animName_Open: YXN_pipei_open
animName_Loop: YXN_pipei_loop
--- !u!1 &3015485591187563618
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 6605999270105133777}
- component: {fileID: 7630183712871860931}
- component: {fileID: 2515020745373521213}
m_Layer: 0
m_Name: SkeletonGraphic (spine_23900) (1)
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 0
--- !u!224 &6605999270105133777
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 3015485591187563618}
m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 1
m_Children:
- {fileID: 1755596749695405114}
m_Father: {fileID: 2713217087351076567}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0.5, y: 0.5}
m_AnchorMax: {x: 0.5, y: 0.5}
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 100, y: 100}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!222 &7630183712871860931
CanvasRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 3015485591187563618}
m_CullTransparentMesh: 1
--- !u!114 &2515020745373521213
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 3015485591187563618}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: d85b887af7e6c3f45a2e2d2920d641bc, type: 3}
m_Name:
m_EditorClassIdentifier:
m_Material: {fileID: 2100000, guid: b66cf7a186d13054989b33a5c90044e4, type: 2}
m_Color: {r: 1, g: 1, b: 1, a: 1}
m_RaycastTarget: 1
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
m_Maskable: 1
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
skeletonDataAsset: {fileID: 11400000, guid: c3708cbf490d8244a939acbf0a352b71, type: 2}
initialSkinName: ppcg
initialFlipX: 0
initialFlipY: 0
startingAnimation:
startingLoop: 0
timeScale: 1
freeze: 0
updateWhenInvisible: 3
unscaledTime: 0
allowMultipleCanvasRenderers: 1
canvasRenderers:
- {fileID: 2500953553794549456}
separatorSlotNames: []
enableSeparatorSlots: 0
separatorParts: []
updateSeparatorPartLocation: 1
meshGenerator:
settings:
useClipping: 1
zSpacing: 0
pmaVertexColors: 1
tintBlack: 0
canvasGroupTintBlack: 0
calculateTangents: 0
addNormals: 0
immutableTriangles: 0
--- !u!1 &3454273065383728345
GameObject:
m_ObjectHideFlags: 0
@@ -68,6 +68,101 @@ public class ChineseChessMainPanel : GameMainPanel_Base
public string animName_Open;
public string animName_Loop;
#region Spine 动画辅助
/// <summary>激活/禁用 Spine(不用时禁用节省性能,用时激活)</summary>
public void ActiveSpine(bool active)
{
if (skeletonGraphic == null) return;
if (skeletonGraphic.gameObject.activeSelf != active)
skeletonGraphic.gameObject.SetActive(active);
}
/// <summary>设置 Spine skin(切换皮肤并重置 slot)</summary>
public void SetSpineSkin(string skinName)
{
if (skeletonGraphic == null || string.IsNullOrEmpty(skinName)) return;
ActiveSpine(true);
var skel = skeletonGraphic.Skeleton;
if (skel == null) return;
skel.SetSkin(skinName);
skel.SetSlotsToSetupPose();
skeletonGraphic.LateUpdate();
}
/// <summary>播放 Spine 动画</summary>
public void PlaySpineAnim(string animName, bool loop = true)
{
if (skeletonGraphic == null || string.IsNullOrEmpty(animName)) return;
ActiveSpine(true);
skeletonGraphic.AnimationState.SetAnimation(0, animName, loop);
}
/// <summary>切换到"正在匹配"状态</summary>
public void Spine_ZhengZaiPiPei()
{
SetSpineSkin(skineName_ZhengZaiPiPei);
PlaySpineAnim(animName_Loop, true);
}
/// <summary>切换到"匹配成功"状态:Open → 完成后播 Close → 禁用</summary>
public void Spine_PiPeiChengGong()
{
SetSpineSkin(skineName_PiPeiChengGong);
PlaySpineAnim(animName_Open, false);
// Open 播完后播 Close,然后禁用 Spine
StartCoroutine(DelayedSpineClose());
}
/// <summary>Open 动画播完后播放 Close 动画并禁用 Spine</summary>
private IEnumerator DelayedSpineClose()
{
// 等 Open 动画播完
yield return new WaitForSeconds(0.5f);
PlaySpineAnim(animName_Close, false);
// Close 播完后禁用 Spine
yield return new WaitForSeconds(0.5f);
ActiveSpine(false);
}
/// <summary>切换到"开始游戏"状态(短暂过渡)</summary>
public void Spine_KaiShi()
{
SetSpineSkin(skineName_KaiShi);
PlaySpineAnim(animName_Open, false);
}
/// <summary>切换到"进行中"状态</summary>
public void Spine_JinXingZhong()
{
SetSpineSkin(skineName_JinXingZhong);
PlaySpineAnim(animName_Loop, true);
}
/// <summary>延迟切换到"进行中"状态(等 KaiShi 过渡动画播完)</summary>
private IEnumerator DelayedSpine_JinXingZhong()
{
// 等待开始过渡动画播放完(默认 0.5s)
yield return new WaitForSeconds(0.5f);
Spine_JinXingZhong();
}
/// <summary>KaiShi 动画流程:Open 播完后切 Loop 播一小会,再播 Close 关闭并禁用</summary>
private IEnumerator DelayedKaiShiLoopToClose()
{
// 等 Open 动画播完
yield return new WaitForSeconds(0.5f);
// 切 Loop 动画播一小会
PlaySpineAnim(animName_Loop, true);
yield return new WaitForSeconds(1f);
// 播 Close 动画并禁用 Spine
PlaySpineAnim(animName_Close, false);
yield return new WaitForSeconds(0.5f);
ActiveSpine(false);
}
#endregion
/// <summary>
/// 加载头像精灵(优先使用缓存,否则异步下载)
/// </summary>
@@ -138,6 +233,8 @@ public class ChineseChessMainPanel : GameMainPanel_Base
if (tmp_WaitTime_Bottom != null) tmp_WaitTime_Bottom.text = "等待玩家进入02:00";
if (tmp_WaitTime_Top != null) tmp_WaitTime_Top.text = "等待玩家进入02:00";
StartWaitCountdown(120, CountdownType.WaitPlayer, "等待玩家进入");
// 正在匹配
Spine_ZhengZaiPiPei();
}
break;
case 2:
@@ -167,6 +264,8 @@ public class ChineseChessMainPanel : GameMainPanel_Base
}
StartWaitCountdown(60, CountdownType.WaitHostStart, "等待房主开始游戏");
// 匹配成功
Spine_PiPeiChengGong();
}
break;
default:
@@ -243,6 +342,8 @@ public class ChineseChessMainPanel : GameMainPanel_Base
// 回到选房间界面时显示等待中(连带父对象)
SetDengDaiZhongActive(true);
subUIManager.ShowAsync(UIPanel.ZGXQDistrict, args);
// 选房阶段不使用 Spine,禁用
ActiveSpine(false);
}
/// <summary>
/// 显示/隐藏等待中(连带控制父对象链)
@@ -269,8 +370,14 @@ public class ChineseChessMainPanel : GameMainPanel_Base
{
// 游戏已开始,停止所有倒计时
StopWaitCountdown();
// 关闭游戏说明面板(双方都准备完毕,进入进行中)
subUIManager.Close(UIPanel.ZGXQGameExplain);
// 双方都准备完毕:播放 KaiShi Spine 动画 Open → Loop 一小会 → Close
Spine_KaiShi();
StartCoroutine(DelayedKaiShiLoopToClose());
btn_QiuHe.onClick.RemoveAllListeners();
btn_RenShu.onClick.RemoveAllListeners();
btn_QiuHe.onClick.AddListener(() =>
@@ -354,6 +461,9 @@ public class ChineseChessMainPanel : GameMainPanel_Base
protected override void OnHide()
{
base.OnHide();
// 退出:播放 Close 动画后禁用 Spine
PlaySpineAnim(animName_Close, false);
ActiveSpine(false);
WebSocketManager.Instance.Close(SocketType.chess);
Screen.orientation = ScreenOrientation.LandscapeLeft;
}
File diff suppressed because it is too large Load Diff
@@ -107,6 +107,14 @@ using Random = UnityEngine.Random;
public class GameMainPanel_FishBase : GameMainPanel_Base
{
[Header("离开游戏倒计时")]
public GameObject obj_DJSPanel;
public TextMeshProUGUI tmp_DJS; //确认(10)
public Button btn_QueRen;
public Button btn_QuXiao;
/// <summary>是否正在退出倒计时(期间屏蔽帧同步和爆奖特效)</summary>
private bool _isLeavingCountdown = false;
private Coroutine _leaveCountdownCoroutine;
[Header("游戏场景")]
public string sceneName;
public GameTypeEnum gameType;
@@ -324,8 +332,7 @@ public class GameMainPanel_FishBase : GameMainPanel_Base
btn_Setting.onClick.AddListener(() => {subUIManager.ShowAsync(UIPanel.SettingPanel); });
btn_TuiChu.onClick.AddListener(() =>
{
WebSocketSendList.LeaveRoom(GameManager.Instance.gameData.currRoomData.roomId, currSitData.user);
CreateFishManager.Instance.fangzhu = false;
StartLeaveCountdown();
});
// 服务器广播监听
@@ -350,6 +357,64 @@ public class GameMainPanel_FishBase : GameMainPanel_Base
});
}
/// <summary>开始离开游戏倒计时:10秒倒计时期间屏蔽帧同步和爆奖特效,结束后点确认才退出</summary>
public void StartLeaveCountdown()
{
if (_isLeavingCountdown) return;
_isLeavingCountdown = true;
if (obj_DJSPanel != null) obj_DJSPanel.SetActive(true);
// 倒计时期间按钮禁用
if (btn_QueRen != null) btn_QueRen.interactable = false;
// 绑定按钮事件
if (btn_QueRen != null)
{
btn_QueRen.onClick.RemoveAllListeners();
btn_QueRen.onClick.AddListener(() =>
{
// 确认:直接退出房间
WebSocketSendList.LeaveRoom(GameManager.Instance.gameData.currRoomData.roomId, currSitData.user);
CreateFishManager.Instance.fangzhu = false;
CancelLeaveCountdown();
});
}
if (btn_QuXiao != null)
{
btn_QuXiao.onClick.RemoveAllListeners();
btn_QuXiao.onClick.AddListener(CancelLeaveCountdown);
}
if (_leaveCountdownCoroutine != null) StopCoroutine(_leaveCountdownCoroutine);
_leaveCountdownCoroutine = StartCoroutine(LeaveCountdownRoutine(10));
}
/// <summary>是否正在离开倒计时</summary>
public bool IsLeavingCountdown() => _isLeavingCountdown;
/// <summary>取消离开倒计时</summary>
public void CancelLeaveCountdown()
{
_isLeavingCountdown = false;
if (_leaveCountdownCoroutine != null)
{
StopCoroutine(_leaveCountdownCoroutine);
_leaveCountdownCoroutine = null;
}
if (obj_DJSPanel != null) obj_DJSPanel.SetActive(false);
}
private IEnumerator LeaveCountdownRoutine(int seconds)
{
int remaining = seconds;
while (remaining > 0)
{
if (tmp_DJS != null) tmp_DJS.text = $"确认({remaining})";
yield return new WaitForSeconds(1f);
remaining--;
}
// 倒计时结束:按钮启用,文本显示"确认"
if (tmp_DJS != null) tmp_DJS.text = "确认";
if (btn_QueRen != null) btn_QueRen.interactable = true;
}
public virtual void OnDestroy()
{
EventCenter.Instance.RemoveEventListener<SyncGameData>(ConstEventString.SyncGameData_Rotate, SyncGameData_Rotate);
@@ -1675,6 +1740,7 @@ public class GameMainPanel_FishBase : GameMainPanel_Base
#region ⑦ 服务器广播处理(NetReceiveModule)
public void SyncGameData_Rotate(SyncGameData sync)
{
if (_isLeavingCountdown) return;
if (sync.sitPos == currSitData.posID) return;
var data = FrameDataParser.Parse<FrameData_Rotate>(sync.frameData);
@@ -1683,6 +1749,7 @@ public class GameMainPanel_FishBase : GameMainPanel_Base
public void SyncGameData_FireBullit(FireCannonSendData sync)
{
if (_isLeavingCountdown) return;
// 机器人开火:extra 中可能没有完整 fireData,从 botLastFireData 还原
if (sync.extra == null || sync.extra.frameData == null)
{
@@ -1715,6 +1782,7 @@ public class GameMainPanel_FishBase : GameMainPanel_Base
/// </summary>
public void SyncGameData_BotFireAngle(SyncGameData sync)
{
if (_isLeavingCountdown) return;
if (sync == null || sync.frameData == null) return;
if (sync.sitPos < 0 || sync.sitPos >= list_Sits.Count) return;
@@ -1730,6 +1798,7 @@ public class GameMainPanel_FishBase : GameMainPanel_Base
public void SyncGameData_ChangeTargetMonster(SyncGameData sync)
{
if (_isLeavingCountdown) return;
if (sync.sitPos == currSitData.posID) return;
var data = FrameDataParser.Parse<FrameData_ChangeAutoTarget>(sync.frameData);
@@ -1754,6 +1823,7 @@ public class GameMainPanel_FishBase : GameMainPanel_Base
public void SyncGameData_RendromDieFish(SyncGameData sync)
{
if (_isLeavingCountdown) return;
var data = JsonConvert.DeserializeObject<FrameData_RandomDieFish>(sync.frameData.ToString());
CreateFishManager.Instance.Id_DieListFish_FX(
@@ -1767,6 +1837,7 @@ public class GameMainPanel_FishBase : GameMainPanel_Base
public void SyncGameData_TurretState(SyncGameData sync)
{
if (_isLeavingCountdown) return;
if (sync.sitPos == currSitData.posID) return;
var data = FrameDataParser.Parse<FrameData_TurretState>(sync.frameData);
@@ -1777,6 +1848,7 @@ public class GameMainPanel_FishBase : GameMainPanel_Base
public void SyncGameData_ChangeTurret(SyncGameData sync)
{
if (_isLeavingCountdown) return;
if (sync.sitPos == currSitData.posID) return;
var data = FrameDataParser.Parse<FrameData_ChangeTurret>(sync.frameData);
@@ -1785,6 +1857,7 @@ public class GameMainPanel_FishBase : GameMainPanel_Base
public void SyncGameData_ChangeWing(SyncGameData sync)
{
if (_isLeavingCountdown) return;
if (sync.sitPos == currSitData.posID) return;
var data = FrameDataParser.Parse<FrameData_ChangeTurret>(sync.frameData);
@@ -1793,6 +1866,7 @@ public class GameMainPanel_FishBase : GameMainPanel_Base
public void SyncGameData_ChangeCannonMultiple(SyncGameData sync)
{
if (_isLeavingCountdown) return;
if (sync.sitPos == currSitData.posID) return;
var data = FrameDataParser.Parse<FrameData_ChangeCannonMultiple>(sync.frameData);
@@ -1816,6 +1890,7 @@ public class GameMainPanel_FishBase : GameMainPanel_Base
public void SyncGameData_FreezeFish(SyncGameData sync)
{
if (_isLeavingCountdown) return;
if (sync.sitPos == currSitData.posID) return; // 自己发的不管
var data = FrameDataParser.Parse<FrameData_FreezeFish>(sync.frameData);
File diff suppressed because it is too large Load Diff
File diff suppressed because one or more lines are too long