using System; using System.Collections; using System.Collections.Generic; using System.Linq; using BestHTTP.SecureProtocol.Org.BouncyCastle.Crypto.Engines; using Common; using DamageNumbersPro; using DragonLi.Core; using Mirror; using UnityEngine; using UnityEngine.PlayerLoop; using XPlugin.Data.JsonLiteDB; using static XPlugin.Data.JsonLiteDB.JsonLiteDB; using LitJson; using Pathfinding; using DarkTonic.MasterAudio; using NaughtyAttributes; using Unity.Mathematics; using UnityEngine.UIElements; using EventDispatcher = DragonLi.Core.EventDispatcher; using Random = UnityEngine.Random; /// /// 游戏状态 /// public enum GameState { None = 0, Playing = 1, /// /// 胜利 /// Victory = 2, /// /// 失败 /// Failur = 3, Settle, Waiting= 5, } public class GameManager : NetworkBehaviour { public static GameManager Ins { get; private set; } #region 预制体 public GameObject redDoorPre; public GameObject blueDoorPre; public GameObject AirdropPlanePre;//空投飞机 public GameObject[] airdropItems;//空头道具 public GameObject startGameItemPre; public GameObject damageNumberPre; public GameObject redDamageNumberPre; // 怪物ui预制体 public GameObject EnemyUIPre; public GameObject roundPlaceDoorPre; #endregion #region 实例化后物体 public GameObject redDoor; public GameObject blueDoor; #endregion // json数据库 private JsonLiteDB DB; // 怪物信息集合 public Dictionary> EnemyInfos = new Dictionary>(); // 枪械信息集合 public Dictionary> GunInfos = new Dictionary>(); /// /// 所有敌人UI /// public Dictionary EnemyUIList = new Dictionary(); public Dictionary SettleInfos = new Dictionary(); public List allPlayers = new List(); // 游玩结束时间 [NonSerialized] [SyncVar] public long vistEnd = 0; [NonSerialized] [SyncVar] public long vistRoundEnd = 0; // 总游玩时长 private int vistAllTime = (int)(60*10f); [NonSerialized] [SyncVar] private int roundAllTime; [SyncVar] public string settleData = ""; [SyncVar] [NonSerialized] public int CurRoundIndex; [SyncVar] public GameState gameState = GameState.None; [NonSerialized] [SyncVar] public int BlueScore = 0; [NonSerialized] [SyncVar] public int RedScore = 0; [NonSerialized] [SyncVar] public int RedRoundScore = 0; [NonSerialized] [SyncVar] public int BlueRoundScore = 0; void Awake() { Ins = this; } void Start() { if (isClient) { UpdateConf(); } if (isServer) { CreateDoor(GameLocal.Ins.redDoorPos, GameLocal.Ins.blueDoorPos,GameLocal.Ins.startGameItemPos); roundAllTime = vistAllTime / 3; } } /// /// 创建门 /// [Server] public void CreateDoor(Transform redPos, Transform bluePos,Transform startGamePos) { SetGameState(GameState.Waiting); if(!isServer) return; //创建红色门 redDoor = Instantiate(redDoorPre,redPos.position,Quaternion.identity); NetworkServer.Spawn(redDoor); //创建蓝色门 blueDoor = Instantiate(blueDoorPre,bluePos.position,Quaternion.identity); NetworkServer.Spawn(blueDoor); //创建开始球 // GameObject gameStartObj = Instantiate(startGameItemPre,startGamePos.position,Quaternion.identity); // gameStartObj.transform.eulerAngles = new Vector3(0f,180f,0f); // NetworkServer.Spawn(gameStartObj); //测试枪械 //GameLocal.Ins.self.GivePistol(); } [ClientRpc] public void RpcShowDamageNumber(float damage, Vector3 pos, bool isCritical) { GameObject prefab = isCritical ? redDamageNumberPre : damageNumberPre; if (prefab == null) return; GameObject obj = Instantiate(prefab, pos, Quaternion.identity); var dmg = obj.GetComponent(); if (dmg != null) { dmg.worldPos = pos; dmg.damageValue = isCritical? damage*2 : damage; dmg.OnStartClient(); } Destroy(obj, 10f); } [Server] public void GameStart() { Debug.Log("开始游戏"); vistEnd = (long)(DateTime.Now.Subtract(new DateTime(1970, 1, 1))).TotalSeconds + vistAllTime; vistRoundEnd = (long)(DateTime.Now.Subtract(new DateTime(1970, 1, 1))).TotalSeconds + roundAllTime; _airdropTime = 25; _curAirdropTime = _airdropTime; ChangeBgmRpc(1); StartAirDrop(); // 延迟1s后给所有人发放武器 CoroutineTaskManager.Instance.WaitSecondTodo(() => { foreach (var player in allPlayers) { if (player != null) { player.HideInvincible(); ShowTeamUI(player.connectionToClient, (int)player.teamType); } } RpcShowSettle(); }, 1f); StartRoundTime(); } [TargetRpc] public void ShowTeamUI(NetworkConnectionToClient conn,int teamId) { EventDispatcher.TriggerEvent("ChangeTeam", teamId); } [Server] public void StartRoundTime() { CurRoundIndex++; CoroutineTaskManager.Instance.WaitSecondTodo(() => { if(gameState==GameState.Waiting) return; StopRound(); }, roundAllTime); } [Server] public void RegisterPlayer(Player p) { if (!allPlayers.Contains(p)) allPlayers.Add(p); } // ★ 玩家离开游戏时调用(在Player.OnStopServer里调用) [Server] public void UnRegisterPlayer(Player p) { if (allPlayers.Contains(p)) allPlayers.Remove(p); } // ★ 游戏开始时调用 → 自动分配队伍 [Server] public void AutoAssignTeams() { int redCount = 0; int blueCount = 0; // 统计已选队伍玩家 foreach (var p in allPlayers) { if (p.teamType == TeamType.Red) redCount++; else if (p.teamType == TeamType.Blue) blueCount++; } int totalPlayers = allPlayers.Count; // ----------------------------- // ★ 第一步:强制平衡队伍(如果人数>=2 且某队==0) // ----------------------------- if (totalPlayers >= 2) { if (redCount == 0 && blueCount > 0) { // 需要从蓝队分一半人到红队 int need = Mathf.Max(1, blueCount / 2); foreach (var p in allPlayers) { if (need <= 0) break; if (p.teamType == TeamType.Blue) { p.teamType = TeamType.Red; need--; redCount++; blueCount--; } } } else if (blueCount == 0 && redCount > 0) { // 需要从红队分一半人到蓝队 int need = Mathf.Max(1, redCount / 2); foreach (var p in allPlayers) { if (need <= 0) break; if (p.teamType == TeamType.Red) { p.teamType = TeamType.Blue; need--; blueCount++; redCount--; } } } } // ----------------------------- // ★ 第二步:给未分配队伍(NONE)的玩家自动分配 // ----------------------------- foreach (var p in allPlayers) { if (p.teamType != TeamType.None) continue; if (redCount < blueCount) { p.teamType = TeamType.Red; redCount++; } else if (blueCount < redCount) { p.teamType = TeamType.Blue; blueCount++; } else { // 平局 → 随机 if (Random.value > 0.5f) { p.teamType = TeamType.Red; redCount++; } else { p.teamType = TeamType.Blue; blueCount++; } } // 给队伍分配武器 GivePistol(p.index); } } [Server] public void CheckGameStart() { int curTeamIndex = 0; bool isStartGame=false; foreach (var player in allPlayers) { if (player.teamType != TeamType.None) curTeamIndex++; } isStartGame=curTeamIndex >= GameLocal.Ins.addPlayerIndex; if (isStartGame) { if(gameState== GameState.Playing) return; AutoAssignTeams(); gameState = GameState.Playing; BlueRoundScore = 0; RedRoundScore = 0; NetworkServer.Destroy(redDoor); NetworkServer.Destroy(blueDoor); RpcShowHUD(); MonoSingleton.Instance.WaitSecondTodo(() => { GameStart(); }, 3f, this); } } [ClientRpc] public void RpcShowSettle() { GameLocal.Ins.Settle.SetActive(true); } [ClientRpc] public void GivePistol(int index) { Debug.Log("创建武器中..."); foreach (var item in NetworkServer.connections.Values) { Player player = item.identity.GetComponent(); if (player != null&&player.index == index) { player.GivePistol(); player.GiveArmor(); } } } [Server] public void SetGameState(GameState state) { gameState = state; } [Server] public void GameOver() { gameState = GameState.Settle; // bool isBlue = GameLocal.Ins.self.teamType == TeamType.Blue; // bool isWin; // if (isBlue) // { // isWin = BlueScore > RedScore; // } // RpcShowWin(isBlue); // if (blueScore >= redScore) // { // RpcShowWin(true); // } // else // { // RpcShowWin(false); // } } [ClientRpc] public void RpcShow() { SettlePanel.Show(); } [ClientRpc] public void ChangeBgmRpc(int i) { GameLocal.Ins.BGMState.StateChange(i); } [Server] public void CreateAirdropPlane(Vector3 startPos, Vector3 endPos) { GameObject airdropPlane = Instantiate(AirdropPlanePre,startPos,quaternion.identity); NetworkServer.Spawn(airdropPlane); AirdropPlane airdropPlaneScript = airdropPlane.GetComponent(); airdropPlaneScript.OnSpawn(endPos); } [Server] public void CreateAirDropItem(int id ,Vector3 startPos) { GameObject airdropItem = Instantiate(airdropItems[id],startPos,quaternion.identity); NetworkServer.Spawn(airdropItem); } /// /// 痛击场上所有敌方单位 /// [Command(requiresAuthority = false)] public void DamageAllEnemy() { } /// /// 更新配置表 /// public void UpdateConf() { string text = Resources.Load("Data").text; if (text != null) { ParseGameJson(text); } } public void ParseGameJson(string text) { DB = new JsonLiteDB(); DB.Load(text); TableReader infoReader = DB["GunsInfo"].GetReader(); while (infoReader.Read()) { GunInfo info = new GunInfo(infoReader); GunInfos.TryGetValue((GunType)info.Type, out Dictionary infoList); if (infoList == null) { Dictionary list = new Dictionary(); list.Add(info.Lvl, info); GunInfos.Add((GunType)info.Type, list); } else { infoList.Add(info.Lvl, info); } } Debug.Log("游戏数值更新 -> complete"); } [Server] public void CreateEnemyUI(Enemy enemy) { GameObject enemyUI = Instantiate(EnemyUIPre); NetworkServer.Spawn(enemyUI); PlayerUI enemyUIScript = enemyUI.GetComponent(); //enemyUIScript.Init(enemy); EnemyUIList.Add(enemy.id, enemyUIScript); } [Server] public void DeleteEnemyUI(int id) { GameObject enemyUI = EnemyUIList[id].gameObject; EnemyUIList.Remove(id); NetworkServer.Destroy(enemyUI); } [ClientRpc] public void RpcShowHUD() { HUDPanel.Show(); } [Server] public void AddData(int playerIndex) { SettleInfos.Add(playerIndex.ToString(), new SettleInfo()); } #region 工具 public string GetLessTimeStr() { string res = ""; if (gameState == GameState.Playing) { res = FormatTime((int)(vistEnd - DateTime.Now.Subtract(new DateTime(1970, 1, 1)).TotalSeconds)); } return res; } public string GetRoundLessTimeStr() { string res = ""; if (gameState == GameState.Playing) { res = FormatTime((int)(vistRoundEnd - DateTime.Now.Subtract(new DateTime(1970, 1, 1)).TotalSeconds)); } return res; } public int GetLessTimeSeconds() { if (isServer) { return (int)(vistEnd - NetworkTime.time); } else { return (int)(vistEnd - NetworkClient.connection.remoteTimeStamp); } } // 时分秒 public string FormatTime(int totalSeconds) { //确保时间不为负 totalSeconds = Mathf.Max(0, totalSeconds); int hours = totalSeconds / 3600; // string hh = hours < 10 ? "0" + hours : hours.ToString(); int minutes = (totalSeconds - hours * 3600) / 60; string mm = minutes < 10f ? "0" + minutes : minutes.ToString(); int seconds = totalSeconds - hours * 3600 - minutes * 60; string ss = seconds < 10 ? "0" + seconds : seconds.ToString(); return string.Format("{0}:{1}", mm, ss); } #endregion [ClientRpc] public void PlaySound2DRPC(string sound) { MasterAudio.PlaySound(sound); } [SyncVar] private int syncRemainingTime; private float _airdropTime;//空投时间 private float _curAirdropTime;//当前空头时间 private bool _isStartAirdrop;//是否开始倒计时空投 public void StartAirDrop() { _isStartAirdrop = true; } public void StopAirDrop() { _isStartAirdrop = false; } void Update() { if (isServer&& gameState==GameState.Playing&&_isStartAirdrop) { //服务器计算剩余时间并同步 //syncRemainingTime = GetLessTimeSeconds(); _curAirdropTime+=Time.deltaTime; if (_curAirdropTime >= _airdropTime) { int airdropPosId = Random.Range(0, 4); int endAirdropPosId =0; if (airdropPosId == 0) endAirdropPosId = 1; if (airdropPosId == 1) endAirdropPosId = 0; if (airdropPosId == 2) endAirdropPosId = 3; if (airdropPosId == 3) endAirdropPosId = 2; CreateAirdropPlane(GameLocal.Ins.startPlanePos[airdropPosId].position,GameLocal.Ins.startPlanePos[endAirdropPosId].position); _curAirdropTime = 0; StopAirDrop(); } } if (isServer) { if (Input.GetKeyDown(KeyCode.Space)) { StopRound(); } } } [Server] public void StartRound() { gameState = GameState.Playing; ChangeBgmRpc(CurRoundIndex+1); vistRoundEnd = (long)(DateTime.Now.Subtract(new DateTime(1970, 1, 1))).TotalSeconds + roundAllTime; foreach (var item in allPlayers) { item.HideInvincible(); } StartRoundTime(); } [Server] public void StopRound() { gameState = GameState.Waiting; Debug.LogError("本回合结束"); ChangeBgmRpc(0); if (CurRoundIndex >= 3) gameState = GameState.Settle; if (BlueRoundScore > RedRoundScore) { BlueScore += 1; } if (BlueRoundScore < RedRoundScore) { RedScore += 1; } if (BlueRoundScore == RedRoundScore&& BlueRoundScore!=0&& RedRoundScore!=0) { RedScore += 1; BlueScore += 1; } foreach (var item in allPlayers) { item.ClearGun(); } MonoSingleton.Instance.WaitSecondTodo(() => { foreach (var item in allPlayers) { RpcPlayerRoundEnd(item.connectionToClient); } }, 1f, this); CreateRoundPlaceDoorPre(); MonoSingleton.Instance.WaitSecondTodo(() => { if(gameState==GameState.Settle) return; BlueRoundScore = 0; RedRoundScore = 0; foreach (var player in allPlayers) { RpcPlayerWaitRoundEnd(player.connectionToClient); } }, 10f, this); MonoSingleton.Instance.WaitSecondTodo(() => { if(gameState==GameState.Settle) return; StartRound(); }, 13f, this); } [TargetRpc] public void RpcPlayerRoundEnd(NetworkConnection target) { EventDispatcher.TriggerEvent("NewRoundStart", CurRoundIndex==3? BlueScore:BlueRoundScore, CurRoundIndex==3? RedScore:RedRoundScore, CurRoundIndex); } [TargetRpc] public void RpcPlayerWaitRoundEnd(NetworkConnection target) { GameLocal.Ins.RoundUI.SetActive(false); EventDispatcher.TriggerEvent("NewWaveStart"); } public Player GetPlayerByIndex(int index) { return allPlayers.FirstOrDefault(p => p.index == index); } #region 回合地图 [Server] public void CreateRoundPlaceDoorPre() { //创建吸收场地黑洞 var roundPlaceDoor = Instantiate(roundPlaceDoorPre,GameLocal.Ins.roundPlaceDoorPos.position,Quaternion.identity); roundPlaceDoor.transform.eulerAngles = GameLocal.Ins.roundPlaceDoorPos.eulerAngles; NetworkServer.Spawn(roundPlaceDoor); foreach (var player in allPlayers) { PlaceFly(player.connectionToClient, roundPlaceDoor.transform.position); } MonoSingleton.Instance.WaitSecondTodo(() => { if (roundPlaceDoor != null) NetworkServer.Destroy(roundPlaceDoor); if(gameState==GameState.Settle) return; // RPC:通知客户端执行地图升起动画 foreach (var player in allPlayers) { RpcMapRise(player.connectionToClient); } }, 8f, this); } [TargetRpc] public void PlaceFly(NetworkConnection target, Vector3 blackHolePos) { GameObject curPlace = GameLocal.Ins.CurPlace(CurRoundIndex); foreach (Transform child in curPlace.GetComponentsInChildren()) { if (child == curPlace.transform) continue; // 跳过根节点 Collider childCollider = child.GetComponent(); if (childCollider != null) childCollider.enabled = false; GameLocal.Ins.AddObject(child, blackHolePos); } } [TargetRpc] public void RpcMapRise(NetworkConnection target) { StartCoroutine(MapRiseCoroutine()); } private IEnumerator MapRiseCoroutine() { GameLocal.Ins.ShowPlace(CurRoundIndex+1); GameObject newMap = GameLocal.Ins.CurPlace(CurRoundIndex+1); // 新地图预先放在场景里 Vector3 startPos = newMap.transform.position; Vector3 endPos = new Vector3(startPos.x, 0f, startPos.z); float duration = 2f; float t = 0f; while (t < duration) { t += Time.deltaTime; float progress = t / duration; // 使用平滑插值(可改成 easeOut) float smooth = Mathf.SmoothStep(0, 1, progress); newMap.transform.position = Vector3.Lerp(startPos, endPos, smooth); yield return null; } newMap.transform.position = endPos; } #endregion #region 分数系统 /// /// 红色方加分 /// /// public void AddRedScore(int score) { RedRoundScore+=score; } /// /// 蓝色方加分 /// /// public void AddBlueScore(int score) { BlueRoundScore+=score; } #endregion }