using System; using GameServer.Client; using UnityEngine; public sealed class StartupSettingsApplier : MonoBehaviour { private const string BootstrapObjectName = "[StartupSettingsApplier]"; private static StartupSettingsApplier _instance; private bool _started; [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)] private static void Bootstrap() { if (_instance != null) { return; } GameObject go = new GameObject(BootstrapObjectName); _instance = go.AddComponent(); DontDestroyOnLoad(go); } private void Start() { if (_started) { return; } _started = true; ApplyStartupSettings(); StartCoroutine(SyncLeaderboardConsentRoutine()); } private void ApplyStartupSettings() { ApplyAndroidLandscapeOrientation(); ApplyStartupFrameRate(); graphicSettings.ApplySavedDisplaySettingsAtStartup(this); ApplySavedAudioSettings(); } // 启动即应用帧率。原本 targetFrameRate 只在设置面板 graphicSettings.Start() 里设置, // 而设置面板不在启动/gameplay 场景中,导致移动端启动后一直停在 Unity 默认 30fps — // 触摸每帧才采样一次(~33ms),表现为点击延迟大、快速连点丢按键。 // 这里在启动时读取与设置面板相同的 PlayerPrefs(frameRateIndex),无存档时默认 90, // 让帧率从进游戏起就正确,且玩家改过的设置下次启动立即生效。不改变原有设置逻辑。 private void ApplyStartupFrameRate() { int[] fpsValues = { 24, 30, 60, 90, 120, 144, 165, 210, 240, 300 }; int index = PlayerPrefs.GetInt("frameRateIndex", -999); if (index < 0 || index > fpsValues.Length + 1) { index = 3; // 默认 90,与 graphicSettings 设置面板一致 } if (index < fpsValues.Length) { QualitySettings.vSyncCount = 0; Application.targetFrameRate = fpsValues[index]; } else if (index == fpsValues.Length) { QualitySettings.vSyncCount = 0; Application.targetFrameRate = -1; // 无限制 } else { QualitySettings.vSyncCount = 1; // 垂直同步 Application.targetFrameRate = -1; } } private void ApplyAndroidLandscapeOrientation() { #if UNITY_ANDROID && !UNITY_EDITOR Screen.autorotateToPortrait = false; Screen.autorotateToPortraitUpsideDown = false; Screen.autorotateToLandscapeLeft = true; Screen.autorotateToLandscapeRight = true; Screen.orientation = ScreenOrientation.AutoRotation; #endif } private void ApplySavedAudioSettings() { audioSettingsPreloader[] preloaders = FindObjectsByType(FindObjectsInactive.Include, FindObjectsSortMode.None); for (int i = 0; i < preloaders.Length; i++) { audioSettingsPreloader preloader = preloaders[i]; if (preloader == null) { continue; } try { preloader.ApplyAudioSettings(); } catch (Exception ex) { Debug.LogWarning($"[StartupSettingsApplier] Failed to apply audio settings via scene preloader: {ex.Message}"); } } } private System.Collections.IEnumerator SyncLeaderboardConsentRoutine() { float timeoutAt = Time.realtimeSinceStartup + 8f; while (NetworkManager.Instance == null && Time.realtimeSinceStartup < timeoutAt) { yield return null; } NetworkManager networkManager = NetworkManager.Instance; if (networkManager == null) { yield break; } bool isJoined = LeaderboardConsentUtility.HasLeaderboardConsent(); var task = networkManager.UpdateLeaderboardMembership(isJoined); while (!task.IsCompleted) { yield return null; } if (task.IsFaulted) { Exception ex = task.Exception; Debug.LogWarning($"[StartupSettingsApplier] Failed to sync leaderboard consent on startup: {ex?.GetBaseException().Message}"); yield break; } LeaderboardMembershipResponse response = task.Result; if (response != null) { LeaderboardConsentUtility.SetLeaderboardConsent(response.is_joined); } } }