Files
5.24BuYu/Assets/MyGame/Scripts/Manager/GameManager.cs
T

465 lines
16 KiB
C#
Raw Normal View History

2026-07-10 23:26:42 +08:00
using Assets.MyGame.Scripts.Net;
2026-02-03 11:35:19 +08:00
using Assets.MyGame.Scripts.Net.API;
2026-04-29 01:18:39 +08:00
using Assets.MyGame.Scripts.Net.API.GameConfig_SubClass;
2026-04-25 18:53:04 +08:00
using CameraShake;
2026-02-03 11:35:19 +08:00
using Newtonsoft.Json;
2026-03-09 09:25:01 +08:00
using System;
2026-07-25 21:04:58 +08:00
using System.Collections;
2026-04-29 01:18:39 +08:00
using System.Collections.Generic;
2026-07-26 22:00:38 +08:00
using System.IO;
2026-06-08 21:23:40 +08:00
using System.Threading.Tasks;
2025-12-11 16:40:24 +08:00
using UnityEngine;
2026-03-18 17:26:14 +08:00
using UnityEngine.InputSystem;
2026-04-17 18:30:51 +08:00
using UnityEngine.Rendering;
2026-02-03 11:35:19 +08:00
using UnityEngine.SceneManagement;
2026-04-21 23:02:51 +08:00
using UnityEngine.UI;
using UnityEngine.Video;
2025-12-11 16:40:24 +08:00
public class GameManager : MonoSingleton<GameManager>
{
2026-06-16 23:45:31 +08:00
[Header("人机")]
2026-07-05 16:07:22 +08:00
public bool enableBot = true; // 人机总开关
public BotConfig botConfig; // 人机AI详细配置(Inspector可调,本地测试用)
2026-07-14 02:48:54 +08:00
[Tooltip("是否使用后端下发的 BotConfig。false=用 Inspector 本地配置(默认,方便测试);true=用后端 GameConfig 返回的 botConfig 覆盖本地")]
public bool useRemoteBotConfig = false;
2026-06-16 23:45:31 +08:00
2026-02-03 11:35:19 +08:00
public GameConfig gameConfig;
2026-07-09 14:29:17 +08:00
public ActivityConfig activityConfig;
2026-06-30 23:57:40 +08:00
public Dictionary<string, FishTypeConfig> dic_fishTypeConfigs = new Dictionary<string, FishTypeConfig>();
2026-04-13 18:30:55 +08:00
public UserData user;
2026-03-09 09:25:01 +08:00
public GameData gameData = new GameData();
2026-02-03 11:35:19 +08:00
public Scene scene;
2026-06-21 18:20:55 +08:00
/// <summary>
/// 头像更新事件(换了头像后触发,所有头像控件统一刷新)
/// </summary>
2026-06-26 17:26:10 +08:00
/// <summary>头像更新事件</summary>
2026-06-21 18:20:55 +08:00
public event Action<Sprite> OnAvatarChanged;
2026-07-05 02:10:44 +08:00
/// <summary>头像框更新事件</summary>
public event Action<Sprite> OnAvatarFrameChanged;
2026-06-26 17:26:10 +08:00
/// <summary>用户数据更新事件(金币/钻石/等级等发生变化时触发)</summary>
public event Action OnUserDataChanged;
2026-06-21 18:20:55 +08:00
/// <summary>头像字典(name → sprite),从 Resources/Avatar 加载</summary>
private Dictionary<string, Sprite> avatarSpriteDict = new Dictionary<string, Sprite>();
2026-07-05 02:10:44 +08:00
/// <summary>头像框字典(name → sprite),从 Resources/AvatarK 加载</summary>
private Dictionary<string, Sprite> avatarFrameDict = new Dictionary<string, Sprite>();
2026-06-21 18:20:55 +08:00
2026-06-21 22:38:18 +08:00
/// <summary>默认头像(新账号无头像时使用)</summary>
private Sprite defaultAvatar;
2026-07-05 02:10:44 +08:00
/// <summary>默认头像框</summary>
private Sprite defaultAvatarFrame;
2026-06-21 22:38:18 +08:00
2026-06-21 18:20:55 +08:00
/// <summary>触发头像更新事件</summary>
public void TriggerAvatarChanged(Sprite sprite)
{
OnAvatarChanged?.Invoke(sprite);
}
2026-04-29 01:18:39 +08:00
public CameraShaker cameraShake;
2026-04-26 20:29:22 +08:00
public Camera ZhuCam;
// // 指定此方法在加载时立即调用,在显示启动动画前之前执行
// [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSplashScreen)]
// private static void BeforeSplashScreen()
// {
// //平台适配
//#if UNITY_WEBGL
// // 如果在 WebGL 平台上运行,立即停止启动动画
// SplashScreen.Stop(SplashScreen.StopBehavior.StopImmediate);
//#else
// // 如果不是在 WebGL 平台上,启动一个异步任务来停止启动动画
// System.Threading.Tasks.Task.Run(() =>
// {
// SplashScreen.Stop(SplashScreen.StopBehavior.StopImmediate);
// });
//#endif
// }
2026-06-21 18:20:55 +08:00
/// <summary>
/// 初始化头像缓存(从 Resources/Avatar 加载所有Sprite)
/// </summary>
public void InitAvatarCache()
{
avatarSpriteDict.Clear();
var sprites = Resources.LoadAll<Sprite>("Avatar");
foreach (var s in sprites)
avatarSpriteDict[s.name] = s;
}
/// <summary>
/// 根据名称获取头像 Sprite
/// </summary>
public Sprite GetAvatarSprite(string avatarName)
{
if (avatarSpriteDict.Count == 0) InitAvatarCache();
2026-06-21 22:38:18 +08:00
if (string.IsNullOrEmpty(avatarName) || !avatarSpriteDict.TryGetValue(avatarName, out var sprite))
sprite = GetDefaultAvatar();
2026-06-21 18:20:55 +08:00
return sprite;
}
2026-06-21 22:38:18 +08:00
private Sprite GetDefaultAvatar()
{
if (defaultAvatar == null && avatarSpriteDict.Count > 0)
{
// 取缓存中第一个头像作为默认
foreach (var kv in avatarSpriteDict)
{
defaultAvatar = kv.Value;
break;
}
}
return defaultAvatar;
}
2026-06-21 18:20:55 +08:00
/// <summary>
/// 更换头像:上传服务端 → 更新本地 → 触发全局刷新
/// </summary>
public void ApplyAvatar(string avatarUrl)
{
APIRequestList.UpdateAvatar(avatarUrl, (result) =>
{
user.BaseInfo.avatarUrl = avatarUrl;
user.BaseInfo.avatarSprite = null;
var sprite = GetAvatarSprite(avatarUrl);
TriggerAvatarChanged(sprite);
});
}
2026-07-05 02:10:44 +08:00
// ==================== 头像框 ====================
/// <summary>触发头像框更新事件</summary>
public void TriggerAvatarFrameChanged(Sprite sprite)
{
OnAvatarFrameChanged?.Invoke(sprite);
}
/// <summary>
/// 初始化头像框缓存(从 Resources/AvatarK 加载所有Sprite)
/// </summary>
public void InitAvatarFrameCache()
{
avatarFrameDict.Clear();
var sprites = Resources.LoadAll<Sprite>("AvatarK");
foreach (var s in sprites)
avatarFrameDict[s.name] = s;
}
/// <summary>
/// 根据名称获取头像框 Sprite
/// </summary>
public Sprite GetAvatarFrameSprite(string avatarFrameName)
{
if (avatarFrameDict.Count == 0) InitAvatarFrameCache();
if (string.IsNullOrEmpty(avatarFrameName) || !avatarFrameDict.TryGetValue(avatarFrameName, out var sprite))
sprite = GetDefaultAvatarFrame();
return sprite;
}
private Sprite GetDefaultAvatarFrame()
{
if (defaultAvatarFrame == null && avatarFrameDict.Count > 0)
{
foreach (var kv in avatarFrameDict)
{
defaultAvatarFrame = kv.Value;
break;
}
}
return defaultAvatarFrame;
}
/// <summary>
/// 更换头像框:上传服务端 → 更新本地 → 触发全局刷新
/// </summary>
public void ApplyAvatarFrame(string avatarFrameName)
{
APIRequestList.UpdateAvatarFrame(avatarFrameName, (result) =>
{
user.BaseInfo.avatarFrame = avatarFrameName;
var sprite = GetAvatarFrameSprite(avatarFrameName);
TriggerAvatarFrameChanged(sprite);
});
}
2026-04-13 18:30:55 +08:00
public void UpdataUserData(UserData user)
{
2026-06-30 23:57:40 +08:00
this.user = user;
OnUserDataChanged?.Invoke();
2026-04-13 18:30:55 +08:00
}
2026-06-08 21:23:40 +08:00
protected override void Awake()
2025-12-26 11:15:56 +08:00
{
2026-07-05 17:39:30 +08:00
// ⭐ botConfig 默认初始化,避免 null 导致技能系统不跑
if (botConfig == null) botConfig = new BotConfig();
2026-06-21 00:07:26 +08:00
// ⭐ 游戏启动时加载已保存的画质和帧率设置
int quality = PlayerPrefs.GetInt("GraphicsQuality", 1);
QualitySettings.SetQualityLevel(quality, true);
int fps = PlayerPrefs.GetInt("TargetFps", 1);
Application.targetFrameRate = fps switch
{
0 => 30,
1 => 60,
2 => -1,
_ => 60
};
2026-06-30 23:57:40 +08:00
2026-07-25 16:13:21 +08:00
// ⭐ 防止手机屏幕自动黑屏休眠
Screen.sleepTimeout = SleepTimeout.NeverSleep;
2026-07-26 22:00:38 +08:00
// ⭐ 启动日志文件自动保存:把所有 Debug.Log/Warning/Error 写入 persistentDataPath/Logs/
InitLogFileWriter();
2026-06-30 23:57:40 +08:00
AudioResourcesData audioResourcesData = new AudioResourcesData();
2026-06-08 21:23:40 +08:00
InItLoad();
//TurretResourcesData turretResourcesData = new TurretResourcesData();
2026-02-26 11:05:56 +08:00
Application.runInBackground = true;
2026-04-03 09:28:13 +08:00
2025-12-26 11:15:56 +08:00
base.Awake();
2026-06-30 23:57:40 +08:00
cameraShake = Camera.main.GetComponent<CameraShaker>();
2026-03-09 09:25:01 +08:00
gameData.currUser = "123";
2026-02-03 11:35:19 +08:00
//string json = Resources.Load<TextAsset>("Config/GameConfig").text;
//gameConfig = JsonConvert.DeserializeObject<APIResponse<GameConfig>>(json).data;
2026-06-30 23:57:40 +08:00
APIManager.Response<GameConfig>(APIType.GameConfig_Get,
res =>
2026-04-29 01:18:39 +08:00
{
2026-06-30 23:57:40 +08:00
gameConfig = res;
var v = CreateFishManager.Instance.CreateDic;
foreach (var item in gameConfig.fishTypeConfigs)
{
dic_fishTypeConfigs[item.fishNumber] = item;
}
2026-07-14 02:48:54 +08:00
// ⭐ 后端 BotConfig 覆盖本地(开关开启且后端返回了 botConfig)
if (useRemoteBotConfig && gameConfig.botConfig != null)
{
botConfig = gameConfig.botConfig;
Debug.Log("[BotConfig] 已使用后端下发的配置覆盖本地");
}
2026-06-30 23:57:40 +08:00
});
2026-02-03 11:35:19 +08:00
2026-07-09 14:29:17 +08:00
APIRequestList.GetActivityConfig(res => { activityConfig = res; });
2026-03-09 09:25:01 +08:00
}
2026-07-26 22:00:38 +08:00
// ⭐ 日志文件写入器:把 Application.logMessageReceived 的日志实时写入文件
private StreamWriter _logFileWriter;
private void InitLogFileWriter()
{
try
{
string logDir = Path.Combine(Application.persistentDataPath, "Logs");
if (!Directory.Exists(logDir)) Directory.CreateDirectory(logDir);
string logPath = Path.Combine(logDir, DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss") + ".log");
_logFileWriter = new StreamWriter(logPath, true) { AutoFlush = true };
Application.logMessageReceived += OnLogMessageReceived;
Debug.Log($"[GameManager] 日志文件已创建: {logPath}");
}
catch (Exception e)
{
Debug.LogError($"[GameManager] 初始化日志文件失败: {e.Message}");
}
}
private void OnLogMessageReceived(string condition, string stackTrace, LogType type)
{
if (_logFileWriter == null) return;
try
{
string prefix = type switch
{
LogType.Error => "[ERROR]",
LogType.Assert => "[ASSERT]",
LogType.Warning => "[WARN]",
LogType.Log => "[LOG]",
LogType.Exception => "[EXCEPTION]",
_ => "[LOG]"
};
_logFileWriter.WriteLine($"{DateTime.Now:HH:mm:ss.fff} {prefix} {condition}");
if (type == LogType.Error || type == LogType.Exception)
{
_logFileWriter.WriteLine($" Stack: {stackTrace}");
}
}
catch { /* 写日志失败不影响游戏运行 */ }
}
private void OnDestroy()
{
if (_logFileWriter != null)
{
Application.logMessageReceived -= OnLogMessageReceived;
try { _logFileWriter.Flush(); _logFileWriter.Close(); } catch { }
_logFileWriter = null;
}
}
2026-06-08 21:23:40 +08:00
public async Task InItLoad()
{
await TurretResourcesData.LoadTurretAssetsData();
}
2026-03-09 09:25:01 +08:00
public void StartGame(Action action)
2025-12-13 18:18:28 +08:00
{
2026-02-03 11:35:19 +08:00
2025-12-26 11:15:56 +08:00
switch (gameData.currGameType)
{
case GameType.JQQD:
break;
case GameType.defaultPanel:
UIManager.Instance.ShowAsync(UIPanel.MainGamePanelDefault);
break;
2026-03-12 10:48:53 +08:00
case GameType.FLZY:
//UIManager.Instance.ShowAsync(UIPanel.MainGamePanelFLZY);
2026-07-10 23:26:42 +08:00
ConnectGameSocket(action);
2025-12-26 11:15:56 +08:00
break;
2026-05-07 03:46:03 +08:00
case GameType.YYSC:
2026-07-10 23:26:42 +08:00
ConnectGameSocket(action);
2025-12-26 11:15:56 +08:00
break;
case GameType.ZGXQ:
2026-07-10 23:26:42 +08:00
//UIManager.Instance.ShowAsync(UIPanel.MainGamePanelZGXQ);
2026-02-13 19:23:01 +08:00
//这里设置触发棋盘的初始化
2025-12-26 11:15:56 +08:00
break;
case GameType.XJMH:
2026-07-10 23:26:42 +08:00
ConnectGameSocket(action);
2025-12-26 11:15:56 +08:00
break;
case GameType.HHWK:
2026-03-18 17:26:14 +08:00
//UIManager.Instance.ShowAsync(UIPanel.MainGamePanelDefault);
2026-07-10 23:26:42 +08:00
ConnectGameSocket(action);
2025-12-26 11:15:56 +08:00
break;
default:
break;
}
2026-03-09 09:25:01 +08:00
2025-12-13 18:18:28 +08:00
}
2026-07-10 23:26:42 +08:00
/// <summary>
/// 通过 /api/Room/Assign 获取房间专属 socket 地址后连接 game socket
/// </summary>
private void ConnectGameSocket(Action action)
{
string roomId = gameData.currRoomData?.roomId;
if (string.IsNullOrEmpty(roomId))
{
#if UNITY_EDITOR
Debug.LogWarning("[GameManager] currRoomData.roomId 为空,使用默认 game socket 地址");
#endif
WebSocketManager.Instance.Connect(SocketType.game, action);
return;
}
APIRequestList.RoomAssign(roomId, url =>
{
if (!string.IsNullOrEmpty(url))
WebSocketManager.Instance.Connect(SocketType.game, url, action);
else
WebSocketManager.Instance.Connect(SocketType.game, action);
});
}
2026-07-20 22:32:52 +08:00
/// <summary>
/// 快速加入:用 /api/Room/QuickJoin 返回的 url 直接连 game socket(无需再调 Assign)
/// </summary>
/// <param name="url">QuickJoin 返回的房间专属 socket 地址</param>
/// <param name="action">连接成功后的回调(一般是 JoinRoom(roomId, userId, -1))</param>
public void StartGameQuickJoin(string url, Action action)
{
switch (gameData.currGameType)
{
case GameType.FLZY:
case GameType.YYSC:
case GameType.XJMH:
case GameType.HHWK:
if (!string.IsNullOrEmpty(url))
WebSocketManager.Instance.Connect(SocketType.game, url, action);
else
WebSocketManager.Instance.Connect(SocketType.game, action);
break;
default:
WebSocketManager.Instance.Connect(SocketType.game, action);
break;
}
}
2025-12-11 16:40:24 +08:00
// Start is called before the first frame update
2026-04-21 23:02:51 +08:00
public Button btn_Skip;
public VideoClip startVideoClip;
2026-07-25 21:04:58 +08:00
IEnumerator yanchi(LoadingPanel loadingPanel)
{
yield return new WaitForSeconds(5);
btn_Skip.gameObject.SetActive(true);
yield return new WaitForSeconds(23f);
loadingPanel.LoadBar(100);
}
2025-12-11 16:40:24 +08:00
void Start()
{
2026-07-25 21:04:58 +08:00
btn_Skip.gameObject.SetActive(false);
2026-04-21 23:02:51 +08:00
var v = UIManager.Instance.ShowLoadPanel(startVideoClip);
2026-07-25 21:04:58 +08:00
//UIManager.Instance.ShowAsync(UIPanel.LoginPanel, null, (a) =>
//{
// v.LoadBar(100);
//}, 28);
StartCoroutine(yanchi(v));
2026-04-21 23:02:51 +08:00
btn_Skip.onClick.AddListener(() =>
{
2026-06-30 23:57:40 +08:00
UIManager.Instance.StopShowPanel();
2026-07-25 21:04:58 +08:00
//UIManager.Instance.ShowAsync(UIPanel.LoginPanel, null, null, 0);
2026-04-21 23:02:51 +08:00
v.LoadBar(100);
});
2025-12-11 16:40:24 +08:00
}
// Update is called once per frame
void Update()
{
2026-03-18 17:26:14 +08:00
#if UNITY_EDITOR
if (Keyboard.current.qKey.wasPressedThisFrame)
{
UIManager.Instance.ShowBoardGongGao("太有实力了dadadadwawafdsdrgdfghdsufhkjghjksdfdgdffghgjhgykhjkhjghjfg");
}
#endif
2025-12-26 11:15:56 +08:00
2026-07-24 01:37:11 +08:00
//// FPS 统计
//fpsFrameCount++;
//fpsTimer += Time.unscaledDeltaTime;
//if (fpsTimer >= 0.5f)
//{
// fps = Mathf.RoundToInt(fpsFrameCount / fpsTimer);
// fpsFrameCount = 0;
// fpsTimer = 0f;
//}
2026-07-11 19:05:27 +08:00
}
2026-07-24 01:37:11 +08:00
//// ===== FPS 显示 =====
//private int fpsFrameCount;
//private float fpsTimer;
//private int fps;
2026-07-11 19:05:27 +08:00
2026-07-24 01:37:11 +08:00
//void OnGUI()
//{
// // 左上角显示帧数
// GUIStyle style = new GUIStyle(GUI.skin.label)
// {
// fontSize = 28,
// fontStyle = FontStyle.Bold
// };
// style.normal.textColor = fps >= 50 ? Color.green : (fps >= 30 ? Color.yellow : Color.red);
// GUI.Label(new Rect(10, 10, 200, 40), "FPS: " + fps, style);
//}
2026-06-28 16:48:18 +08:00
public void Logout(Action onDone = null)
{
// 关闭所有 WebSocket 连接
WebSocketManager.Instance.CloseAll();
// 清空用户数据
user = null;
// 关闭所有 UI
UIManager.Instance.CloseAll();
// 切换到登录界面
UIManager.Instance.ShowAsync(UIPanel.LoginPanel, null, (panel) =>
{
onDone?.Invoke();
});
}
2025-12-11 16:40:24 +08:00
}