Files
5.24BuYu/Assets/MyGame/aa/UI/Game/ChineseChess/ChineseChessMainPanel.cs
T
2026-07-12 00:26:17 +08:00

471 lines
16 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using Assets.MyGame.Scripts.Net;
using Newtonsoft.Json;
using Spine.Unity;
using System;
using System.Collections;
using System.Collections.Generic;
using TMPro;
using UnityEngine;
using UnityEngine.UI;
using static Assets.MyGame.Scripts.Net.WebResponses.ChineseChess.ChineseChessGameClassList;
public class HintData
{
public ChessGameRoom roomData;
public int hintType; //1求和2认输
}
/// <summary>
/// GoldConfigRequired Socket 推送数据
/// </summary>
[Serializable]
public class GoldConfigRequiredData
{
public string targetUserId;
public ChessGameRoom room;
}
public enum CountdownType
{
WaitPlayer, // 等待玩家进入,超时 → 退出到选房
WaitHostStart, // 等待房主开始游戏,超时 → 退出到选房
WaitSetCoin, // 等待设置金额,超时 → 退出到选房
WaitOpponent, // 等待对手操作,超时 → 自动处理
}
public class ChineseChessMainPanel : GameMainPanel_Base
{
public Button btn_QiuHe;
public Button btn_RenShu;
public Button btn_HuiQi;
public Button btn_SheZhi;
public Button btn_GuiZe;
public Button btn_StartGame;
public Image img_DengDaiZhong;
public ChessGameRoom roomData;
public static Dictionary<string,ChessGameRoom> dic_RoomData=new Dictionary<string, ChessGameRoom>();
public Button btn_FanHuiDaTing;
[Header("Bottom")]
public TextMeshProUGUI tmp_Name_Bottom;
public TextMeshProUGUI tmp_CoinCount_Bottom;
public TextMeshProUGUI tmp_WaitTime_Bottom;
public Image img_Title_Bottom;
[Header("Top")]
public TextMeshProUGUI tmp_Name_Top;
public TextMeshProUGUI tmp_CoinCount_Top;
public TextMeshProUGUI tmp_WaitTime_Top;
public Image img_Title_Top;
private Coroutine waitCountdown;
public SkeletonGraphic skeletonGraphic;
public string skineName_JinXingZhong;
public string skineName_KaiShi;
public string skineName_PiPeiChengGong;
public string skineName_ZhengZaiPiPei;
public string animName_Close;
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>
public static void LoadAvatar(Image target, string avatarUrl, System.Action<Sprite> onLoaded = null)
{
if (target == null) return;
var sprite = GameManager.Instance.GetAvatarSprite(avatarUrl);
if (sprite != null)
{
target.sprite = sprite;
onLoaded?.Invoke(sprite);
return;
}
// 缓存未命中,通过 DownSprite 协程异步加载
GameManager.Instance.StartCoroutine(LoadAvatarCoroutine(target, avatarUrl, onLoaded));
}
private static IEnumerator LoadAvatarCoroutine(Image target, string avatarUrl, System.Action<Sprite> onLoaded)
{
// 构造一个临时的 UserBaseInfo 对象来复用 DownSprite 逻辑
var tempInfo = new UserBaseInfo { avatarUrl = avatarUrl };
yield return tempInfo.DownSprite(sprite =>
{
if (sprite != null && target != null)
target.sprite = sprite;
onLoaded?.Invoke(sprite);
});
}
public static string FormatGold(long gold)
{
if (gold >= 100000000)
{
// 截取两位小数,不四舍五入
long yi = gold / 100000000;
long remainder = gold % 100000000;
long decimals = remainder * 100 / 100000000;
if (decimals > 0)
return $"{yi}.{decimals:D2}亿";
return $"{yi}亿";
}
if (gold >= 10000)
{
// 截取两位小数,不四舍五入
long wan = gold / 10000;
long remainder = gold % 10000;
long decimals = remainder * 100 / 10000; // 两位小数
if (decimals > 0)
return $"{wan}.{decimals:D2}万";
return $"{wan}万";
}
return gold.ToString();
}
public void StartGame(string msg)
{
var va = JsonConvert.DeserializeObject<WebSocketResponseBase<ChessGameRoom>>(msg.ToString());
ChessGameRoom chessGameRoom = va.arguments[0];
UpdateUserData(chessGameRoom);
switch (chessGameRoom.users.Count)
{
case 1:
{
tmp_Name_Top.text = "等待玩家进入";
tmp_CoinCount_Top.text = "";
img_Title_Top.gameObject.SetActive(false);
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:
{
StopWaitCountdown();
img_Title_Top.gameObject.SetActive(true);
// 判断是否为房主
bool isRoomOwner = chessGameRoom.users.Count > 0
&& chessGameRoom.users[0].userId == GameManager.Instance.user.BaseInfo.number;
if (isRoomOwner)
{
btn_StartGame.gameObject.SetActive(true);
btn_StartGame.onClick.RemoveAllListeners();
btn_StartGame.onClick.AddListener(() =>
{
// 房主点击开始 → 发送 StartGame Socket 请求
btn_StartGame.gameObject.SetActive(false);
StopWaitCountdown();
WebSocketSendList.StartGameChineseChess(chessGameRoom.roomId, GameManager.Instance.user.BaseInfo.number);
});
}
else
{
btn_StartGame.gameObject.SetActive(false);
}
StartWaitCountdown(60, CountdownType.WaitHostStart, "等待房主开始游戏");
// 匹配成功
Spine_PiPeiChengGong();
}
break;
default:
break;
}
}
/// <summary>
/// 服务端推送 GoldConfigRequired → 显示金币设置面板(红方配置)
/// data: { targetUserId, room }
/// </summary>
public void OnGoldConfigRequired(string msg)
{
StopWaitCountdown();
var result = JsonConvert.DeserializeObject<WebSocketResponseBase<GoldConfigRequiredData>>(msg);
var data = result.arguments[0];
// 更新房间数据
if (data.room != null)
{
roomData = data.room;
UpdateUserData(data.room);
}
// 判断当前用户是否为红方(targetUserId)
bool isRedSide = data.targetUserId == GameManager.Instance.user.BaseInfo.number;
if (isRedSide)
{
// 红方显示金币设置面板
subUIManager.ShowAsync(UIPanel.ZGXQSelectCoin, roomData);
StartWaitCountdown(60, CountdownType.WaitSetCoin, "等待设置金额");
}
else
{
// 黑方等待红方设置金币
StartWaitCountdown(60, CountdownType.WaitSetCoin, "等待对方设置金额");
}
}
public void UpdateUserData(ChessGameRoom chessGameRoom)
{
foreach (var item in chessGameRoom.users)
{
if (item.userId == GameManager.Instance.user.BaseInfo.number)
{
tmp_Name_Bottom.text ="ID:" + item.userId;
tmp_CoinCount_Bottom.text = FormatGold(item.gold);
LoadAvatar(img_Title_Bottom, item.userAvatar);
}
else
{
tmp_Name_Top.text ="ID:" + item.userId;
tmp_CoinCount_Top.text = FormatGold(item.gold);
LoadAvatar(img_Title_Top, item.userAvatar);
}
}
}
public override void Awake()
{
base.Awake();
btn_FanHuiDaTing.onClick.AddListener(() =>
{
UIManager.Instance.Close(UIPanel.MainGamePanelZGXQ);
});
btn_GuiZe.onClick.AddListener(() =>
{
subUIManager.ShowAsync(UIPanel.ZGXQIntroduce);
});
}
protected override void OnShow(object args = null)
{
base.OnShow(args);
// 回到选房间界面时显示等待中(连带父对象)
SetDengDaiZhongActive(true);
subUIManager.ShowAsync(UIPanel.ZGXQDistrict, args);
// 选房阶段不使用 Spine,禁用
ActiveSpine(false);
}
/// <summary>
/// 显示/隐藏等待中(连带控制父对象链)
/// </summary>
public void SetDengDaiZhongActive(bool active)
{
if (img_DengDaiZhong != null)
{
img_DengDaiZhong.gameObject.SetActive(active);
}
}
public void IsShowHint(string id,int type)
{
if (GameManager.Instance.user.BaseInfo.number!=id)
{
subUIManager.ShowAsync(UIPanel.ZGXQHint, new HintData
{
roomData = roomData,
hintType = type
});
}
}
public void InitRoomData()
{
// 游戏已开始,停止所有倒计时
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(() =>
{
WebSocketSendList.GameOperationChineseChess(roomData.roomId,1,GameManager.Instance.user.BaseInfo.number);
});
btn_RenShu.onClick.AddListener(() =>
{
//WebSocketSendList.GameOperationChineseChess(roomData.roomId, 2, GameManager.Instance.user.BaseInfo.number);
subUIManager.ShowAsync(UIPanel.ZGXQHint, new HintData
{
roomData = roomData,
hintType = 2
});
});
}
public void Start()
{
}
/// <summary>
/// 开始等待倒计时
/// </summary>
/// <param name="seconds">总秒数</param>
/// <param name="type">倒计时类型</param>
/// <param name="waitMsg">等待时显示的文字,如"等待玩家进入"</param>
public void StartWaitCountdown(int seconds, CountdownType type = CountdownType.WaitPlayer, string waitMsg = null)
{
if (waitCountdown != null) StopCoroutine(waitCountdown);
waitCountdown = StartCoroutine(WaitCountdownRoutine(seconds, type, waitMsg));
}
/// <summary>
/// 停止等待倒计时
/// </summary>
public void StopWaitCountdown()
{
if (waitCountdown != null)
{
StopCoroutine(waitCountdown);
waitCountdown = null;
}
if (tmp_WaitTime_Bottom != null) tmp_WaitTime_Bottom.text = "";
if (tmp_WaitTime_Top != null) tmp_WaitTime_Top.text = "";
}
private IEnumerator WaitCountdownRoutine(int totalSeconds, CountdownType type, string waitMsg)
{
int remaining = totalSeconds;
while (remaining >= 0)
{
string timeStr = $"{remaining / 60:D2}:{remaining % 60:D2}";
switch (type)
{
default:
string displayText = string.IsNullOrEmpty(waitMsg) ? timeStr : $"{waitMsg}{timeStr}";
if (tmp_WaitTime_Bottom != null)
tmp_WaitTime_Bottom.text = displayText;
if (tmp_WaitTime_Top != null)
tmp_WaitTime_Top.text = displayText;
break;
}
yield return new WaitForSeconds(1f);
remaining--;
}
switch (type)
{
default:
// 超时:返回选房间界面
subUIManager.CloseAll();
WebSocketSendList.GetRoomListChineseChess(1);
break;
}
}
protected override void OnHide()
{
base.OnHide();
// 退出:播放 Close 动画后禁用 Spine
PlaySpineAnim(animName_Close, false);
ActiveSpine(false);
WebSocketManager.Instance.Close(SocketType.chess);
Screen.orientation = ScreenOrientation.LandscapeLeft;
}
}