ui基本完毕,修了一大把的bug

This commit is contained in:
FloatGaming
2026-07-13 02:28:39 +08:00
parent 1e20d73e90
commit fd22501f71
958 changed files with 378289 additions and 41038 deletions
@@ -0,0 +1,98 @@
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<StartupSettingsApplier>();
DontDestroyOnLoad(go);
}
private void Start()
{
if (_started)
{
return;
}
_started = true;
ApplyStartupSettings();
StartCoroutine(SyncLeaderboardConsentRoutine());
}
private void ApplyStartupSettings()
{
graphicSettings.ApplySavedDisplaySettingsAtStartup(this);
ApplySavedAudioSettings();
}
private void ApplySavedAudioSettings()
{
audioSettingsPreloader[] preloaders = FindObjectsByType<audioSettingsPreloader>(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);
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 4e9ad3d519677da4bb6487b008a1eddb
+17 -4
View File
@@ -37,6 +37,7 @@ public class controllerSettings : MonoBehaviour
private const float NoteSpeedMin = 0.5f;
private const float NoteSpeedMax = 2f;
private const string NoteSpeedPrefKey = "noteSpeedMultiplier";
private const float NoteSpeedDefault = 1.0f;
// continuous change
private Coroutine continuousChangeCoroutine = null;
@@ -77,7 +78,8 @@ public class controllerSettings : MonoBehaviour
{
noteSpeedMultipler_slider.minValue = NoteSpeedMin;
noteSpeedMultipler_slider.maxValue = NoteSpeedMax;
float saved = PlayerPrefs.GetFloat(NoteSpeedPrefKey, 1.0f);
EnsureDefaultNoteSpeedPreference();
float saved = PlayerPrefs.GetFloat(NoteSpeedPrefKey, NoteSpeedDefault);
saved = Mathf.Clamp(saved, NoteSpeedMin, NoteSpeedMax);
noteSpeedMultipler_slider.value = saved;
UpdateNoteSpeedText(saved);
@@ -494,9 +496,20 @@ public class controllerSettings : MonoBehaviour
private void ResetNoteSpeed()
{
if (noteSpeedMultipler_slider == null) return;
noteSpeedMultipler_slider.value = 1.0f; // triggers change and save
SaveNoteSpeedValue(1.0f);
UpdateNoteSpeedText(1.0f);
noteSpeedMultipler_slider.value = NoteSpeedDefault; // triggers change and save
SaveNoteSpeedValue(NoteSpeedDefault);
UpdateNoteSpeedText(NoteSpeedDefault);
}
private static void EnsureDefaultNoteSpeedPreference()
{
if (PlayerPrefs.HasKey(NoteSpeedPrefKey))
{
return;
}
PlayerPrefs.SetFloat(NoteSpeedPrefKey, NoteSpeedDefault);
PlayerPrefs.Save();
}
private void AddButtonContinuousEvents(Button btn, float delta)
+195 -18
View File
@@ -1,32 +1,205 @@
using System;
using Bansonic;
using GameServer.Client;
using UnityEngine;
using UnityEngine.UI;
using TMPro;
public class gameSettings : MonoBehaviour
{
[Header("multiNotesSigns")]
public Toggle multiNoteNoticeToggle;
public Text multiNoteNoticeText; // Using Legacy Text object
public Text multiNoteNoticeText;
private const string PrefKey_EnableSyncNotePrefab = "EnableSyncNotePrefab";
[Header("joinRankingList")]
public Toggle user_accept_joinRankingList;
public Text jrlWarningText;
void Start()
private const string PrefKeyEnableSyncNotePrefab = "EnableSyncNotePrefab";
private bool _isUpdatingJoinRankingToggle;
private void Awake()
{
if (multiNoteNoticeToggle != null)
{
// Initialize toggle state from PlayerPrefs, default to 1 (true)
bool isEnabled = PlayerPrefs.GetInt(PrefKey_EnableSyncNotePrefab, 1) == 1;
multiNoteNoticeToggle.isOn = isEnabled;
multiNoteNoticeToggle.onValueChanged.RemoveListener(HandleMultiNoteNoticeToggleChanged);
multiNoteNoticeToggle.onValueChanged.AddListener(HandleMultiNoteNoticeToggleChanged);
}
// Initialize text based on current toggle value
UpdateText(isEnabled);
if (user_accept_joinRankingList != null)
{
user_accept_joinRankingList.onValueChanged.RemoveListener(HandleJoinRankingListToggleChanged);
user_accept_joinRankingList.onValueChanged.AddListener(HandleJoinRankingListToggleChanged);
}
}
// Add listener to save value and update text when changed
multiNoteNoticeToggle.onValueChanged.AddListener((isOn) =>
private void OnEnable()
{
RefreshMultiNoteNoticeState();
_ = RefreshJoinRankingStateFromServerAsync();
}
private void OnDestroy()
{
if (multiNoteNoticeToggle != null)
{
multiNoteNoticeToggle.onValueChanged.RemoveListener(HandleMultiNoteNoticeToggleChanged);
}
if (user_accept_joinRankingList != null)
{
user_accept_joinRankingList.onValueChanged.RemoveListener(HandleJoinRankingListToggleChanged);
}
}
private void RefreshMultiNoteNoticeState()
{
if (multiNoteNoticeToggle == null)
{
return;
}
bool isEnabled = PlayerPrefs.GetInt(PrefKeyEnableSyncNotePrefab, 1) == 1;
multiNoteNoticeToggle.SetIsOnWithoutNotify(isEnabled);
UpdateText(isEnabled);
}
private async Awaitable RefreshJoinRankingStateFromServerAsync()
{
if (user_accept_joinRankingList == null)
{
return;
}
try
{
NetworkManager nm = NetworkManager.Instance;
if (nm == null)
{
PlayerPrefs.SetInt(PrefKey_EnableSyncNotePrefab, isOn ? 1 : 0);
PlayerPrefs.Save();
UpdateText(isOn);
});
RefreshJoinRankingListStateFromLocal();
await RefreshJoinRankingWarningTextAsync(null);
return;
}
LeaderboardMembershipResponse resp = await nm.GetLeaderboardMembership();
bool isJoined = resp != null ? resp.is_joined : LeaderboardConsentUtility.HasLeaderboardConsent();
LeaderboardConsentUtility.SetLeaderboardConsent(isJoined);
_isUpdatingJoinRankingToggle = true;
user_accept_joinRankingList.SetIsOnWithoutNotify(isJoined);
_isUpdatingJoinRankingToggle = false;
await RefreshJoinRankingWarningTextAsync(resp);
}
catch (Exception ex)
{
Debug.LogWarning($"[gameSettings] Failed to refresh leaderboard membership state: {ex.Message}");
RefreshJoinRankingListStateFromLocal();
await RefreshJoinRankingWarningTextAsync(null);
}
}
private void RefreshJoinRankingListStateFromLocal()
{
if (user_accept_joinRankingList == null)
{
return;
}
_isUpdatingJoinRankingToggle = true;
user_accept_joinRankingList.SetIsOnWithoutNotify(LeaderboardConsentUtility.HasLeaderboardConsent());
_isUpdatingJoinRankingToggle = false;
}
private async Awaitable RefreshJoinRankingWarningTextAsync(LeaderboardMembershipResponse membership)
{
if (jrlWarningText == null)
{
return;
}
if (membership != null && membership.changed_today && !string.IsNullOrWhiteSpace(membership.next_change_at))
{
jrlWarningText.text = BuildChangeCooldownText(membership.next_change_at);
return;
}
jrlWarningText.text = "下次排行榜更新时间:正在获取...";
try
{
jrlWarningText.text = membership != null && !string.IsNullOrWhiteSpace(membership.next_settle_at)
? "下次排行榜更新时间:" + membership.next_settle_at
: await LeaderboardConsentUtility.BuildNextSettleWarningTextAsync();
}
catch (Exception ex)
{
Debug.LogWarning($"[gameSettings] Failed to refresh ranking warning text: {ex.Message}");
jrlWarningText.text = "下次排行榜更新时间:--";
}
}
private static string BuildChangeCooldownText(string nextChangeAt)
{
if (DateTime.TryParse(nextChangeAt, out DateTime parsed))
{
return $"{parsed.Hour}时{parsed.Minute:D2}分后可切换";
}
return "今日已切换";
}
private void HandleMultiNoteNoticeToggleChanged(bool isOn)
{
PlayerPrefs.SetInt(PrefKeyEnableSyncNotePrefab, isOn ? 1 : 0);
PlayerPrefs.Save();
UpdateText(isOn);
}
private async void HandleJoinRankingListToggleChanged(bool isOn)
{
if (_isUpdatingJoinRankingToggle)
{
return;
}
try
{
NetworkManager nm = NetworkManager.Instance;
if (nm == null)
{
LeaderboardConsentUtility.SetLeaderboardConsent(isOn);
await RefreshJoinRankingWarningTextAsync(null);
return;
}
LeaderboardMembershipResponse resp = await nm.UpdateLeaderboardMembership(isOn);
bool finalJoined = resp != null ? resp.is_joined : isOn;
LeaderboardConsentUtility.SetLeaderboardConsent(finalJoined);
_isUpdatingJoinRankingToggle = true;
user_accept_joinRankingList.SetIsOnWithoutNotify(finalJoined);
_isUpdatingJoinRankingToggle = false;
if (resp != null && !resp.success)
{
if (!string.IsNullOrWhiteSpace(resp.next_change_at))
{
gNotice.warning.display(BuildChangeCooldownText(resp.next_change_at));
}
else if (!string.IsNullOrWhiteSpace(resp.message))
{
gNotice.warning.display(resp.message);
}
}
await RefreshJoinRankingWarningTextAsync(resp);
}
catch (Exception ex)
{
Debug.LogWarning($"[gameSettings] Failed to update leaderboard membership: {ex.Message}");
gNotice.error.display("排行榜加入状态更新失败");
RefreshJoinRankingListStateFromLocal();
await RefreshJoinRankingWarningTextAsync(null);
}
}
@@ -34,14 +207,18 @@ public class gameSettings : MonoBehaviour
{
if (multiNoteNoticeText != null)
{
multiNoteNoticeText.text = isOn ? "已启用多押指示器" : "已禁用多押指示器";
multiNoteNoticeText.text = isOn ? "已开启多押提示音符同步显示" : "已关闭多押提示音符同步显示";
}
if (multiNoteNoticeToggle == null)
{
return;
}
// Also update the Toggle's own label text if it exists (usually a child object)
Text toggleLabel = multiNoteNoticeToggle.GetComponentInChildren<Text>();
if (toggleLabel != null)
{
toggleLabel.text = isOn ? "启" : "禁用";
toggleLabel.text = isOn ? "已开启" : "已关闭";
}
}
}
+183 -92
View File
@@ -8,6 +8,11 @@ using System.Runtime.InteropServices;
public class graphicSettings : MonoBehaviour
{
private const string PrefKeyScreenMode = "screenMode";
private const string PrefKeyResolutionIndex = "resolutionIndex";
private const string PrefKeyCustomResW = "customResW";
private const string PrefKeyCustomResH = "customResH";
public Dropdown screenMode_Dropdown;
public Dropdown resolution_Dropdown;
public Dropdown frameRate_Dropdown;
@@ -30,6 +35,22 @@ public class graphicSettings : MonoBehaviour
lastScreenSize = new Vector2Int(Screen.width, Screen.height);
}
public static void ApplySavedDisplaySettingsAtStartup(MonoBehaviour coroutineRunner)
{
int screenMode = PlayerPrefs.GetInt(PrefKeyScreenMode, 0);
screenMode = Mathf.Clamp(screenMode, 0, 2);
try
{
ApplyScreenModeStatic(screenMode, coroutineRunner);
ApplySavedResolutionStatic();
}
catch (Exception ex)
{
Debug.LogWarning($"[graphicSettings] Failed to apply saved display settings at startup: {ex}");
}
}
void OnDestroy()
{
if (screenMode_Dropdown != null)
@@ -99,7 +120,7 @@ public class graphicSettings : MonoBehaviour
int current = MapFullScreenModeToIndex(Screen.fullScreenMode);
if (PlayerPrefs.HasKey("screenMode"))
{
int saved = PlayerPrefs.GetInt("screenMode", current);
int saved = PlayerPrefs.GetInt(PrefKeyScreenMode, current);
saved = Mathf.Clamp(saved, 0, options.Count - 1);
current = saved;
ApplyScreenMode(current);
@@ -129,7 +150,7 @@ public class graphicSettings : MonoBehaviour
private void OnScreenModeChanged(int index)
{
ApplyScreenMode(index);
PlayerPrefs.SetInt("screenMode", index);
PlayerPrefs.SetInt(PrefKeyScreenMode, index);
PlayerPrefs.Save();
bool isWindowed = (index == 0);
@@ -141,96 +162,17 @@ public class graphicSettings : MonoBehaviour
private void ApplyScreenMode(int index)
{
try
{
if (index == 0)
{
Screen.fullScreenMode = FullScreenMode.Windowed;
Screen.fullScreen = false;
StopAllCoroutines();
StartCoroutine(ApplyWindowStyleNextFrame(true));
Screen.SetResolution(Screen.width, Screen.height, FullScreenMode.Windowed, Screen.currentResolution.refreshRateRatio);
}
else if (index == 1)
{
Screen.fullScreenMode = FullScreenMode.ExclusiveFullScreen;
Screen.fullScreen = true;
Resolution res = Screen.currentResolution;
Screen.SetResolution(res.width, res.height, FullScreenMode.ExclusiveFullScreen, res.refreshRateRatio);
}
else if (index == 2)
{
Screen.fullScreenMode = FullScreenMode.FullScreenWindow;
Screen.fullScreen = true;
Resolution res = Screen.currentResolution;
Screen.SetResolution(res.width, res.height, FullScreenMode.FullScreenWindow, res.refreshRateRatio);
StopAllCoroutines();
StartCoroutine(ApplyWindowStyleNextFrame(false));
}
}
catch (System.Exception ex)
{
Debug.LogWarning($"ApplyScreenMode failed: {ex}");
}
ApplyScreenModeStatic(index, this);
}
private System.Collections.IEnumerator ApplyWindowStyleNextFrame(bool enable)
{
yield return null;
yield return new WaitForSeconds(0.05f);
EnableWindowedModeResizable(enable);
yield return ApplyWindowStyleNextFrameStatic(enable);
}
private void EnableWindowedModeResizable(bool enable)
{
#if UNITY_STANDALONE_WIN && !UNITY_EDITOR
try
{
IntPtr hWnd = GetForegroundWindow();
if (hWnd == IntPtr.Zero) return;
const int GWL_STYLE = -16;
const int WS_OVERLAPPEDWINDOW = unchecked((int)0x00CF0000);
const int WS_POPUP = unchecked((int)0x80000000);
if (IntPtr.Size == 8)
{
long style = GetWindowLongPtr64(hWnd, GWL_STYLE);
if (enable)
{
style &= ~((long)WS_POPUP);
style |= WS_OVERLAPPEDWINDOW;
}
else
{
style &= ~((long)WS_OVERLAPPEDWINDOW);
style |= (long)WS_POPUP;
}
SetWindowLongPtr64(hWnd, GWL_STYLE, style);
SetWindowPos(hWnd, IntPtr.Zero, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
}
else
{
int style = GetWindowLong32(hWnd, GWL_STYLE);
if (enable)
{
style &= ~WS_POPUP;
style |= WS_OVERLAPPEDWINDOW;
}
else
{
style &= ~((int)WS_OVERLAPPEDWINDOW);
style |= (int)WS_POPUP;
}
SetWindowLong32(hWnd, GWL_STYLE, style);
SetWindowPos(hWnd, IntPtr.Zero, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
}
}
catch (Exception e)
{
Debug.LogWarning($"EnableWindowedModeResizable failed: {e}");
}
#endif
EnableWindowedModeResizableStatic(enable);
}
private void InitResolutionDropdown()
@@ -295,7 +237,7 @@ public class graphicSettings : MonoBehaviour
}
}
int savedResIndex = PlayerPrefs.GetInt("resolutionIndex", -2);
int savedResIndex = PlayerPrefs.GetInt(PrefKeyResolutionIndex, -2);
if (savedResIndex >= 0 && savedResIndex < availableResolutions.Count)
{
resolution_Dropdown.SetValueWithoutNotify(savedResIndex);
@@ -305,8 +247,8 @@ public class graphicSettings : MonoBehaviour
}
else if (savedResIndex == -1)
{
int cw = PlayerPrefs.GetInt("customResW", -1);
int ch = PlayerPrefs.GetInt("customResH", -1);
int cw = PlayerPrefs.GetInt(PrefKeyCustomResW, -1);
int ch = PlayerPrefs.GetInt(PrefKeyCustomResH, -1);
if (cw > 0 && ch > 0)
{
Screen.SetResolution(cw, ch, Screen.fullScreenMode, Screen.currentResolution.refreshRateRatio);
@@ -325,7 +267,7 @@ public class graphicSettings : MonoBehaviour
}
resolution_Dropdown.onValueChanged.AddListener(OnResolutionChanged);
int screenMode = PlayerPrefs.GetInt("screenMode", MapFullScreenModeToIndex(Screen.fullScreenMode));
int screenMode = PlayerPrefs.GetInt(PrefKeyScreenMode, MapFullScreenModeToIndex(Screen.fullScreenMode));
bool isWindowed = (screenMode == 0) || (Screen.fullScreenMode == FullScreenMode.Windowed);
if (resolutionLock_Image != null)
resolutionLock_Image.gameObject.SetActive(!isWindowed);
@@ -354,9 +296,9 @@ public class graphicSettings : MonoBehaviour
if (index < 0) return;
if (index >= availableResolutions.Count)
{
PlayerPrefs.SetInt("resolutionIndex", -1);
PlayerPrefs.SetInt("customResW", Screen.width);
PlayerPrefs.SetInt("customResH", Screen.height);
PlayerPrefs.SetInt(PrefKeyResolutionIndex, -1);
PlayerPrefs.SetInt(PrefKeyCustomResW, Screen.width);
PlayerPrefs.SetInt(PrefKeyCustomResH, Screen.height);
PlayerPrefs.Save();
return;
}
@@ -364,7 +306,7 @@ public class graphicSettings : MonoBehaviour
var r = availableResolutions[index];
FullScreenMode mode = Screen.fullScreenMode;
Screen.SetResolution(r.x, r.y, mode, Screen.currentResolution.refreshRateRatio);
PlayerPrefs.SetInt("resolutionIndex", index);
PlayerPrefs.SetInt(PrefKeyResolutionIndex, index);
PlayerPrefs.Save();
RemoveTempFreeOptionIfExists();
@@ -484,6 +426,155 @@ public class graphicSettings : MonoBehaviour
}
}
private static void ApplyScreenModeStatic(int index, MonoBehaviour coroutineRunner)
{
try
{
if (index == 0)
{
Screen.fullScreenMode = FullScreenMode.Windowed;
Screen.fullScreen = false;
Screen.SetResolution(Screen.width, Screen.height, FullScreenMode.Windowed, Screen.currentResolution.refreshRateRatio);
if (coroutineRunner != null)
{
coroutineRunner.StartCoroutine(ApplyWindowStyleNextFrameStatic(true));
}
}
else if (index == 1)
{
Screen.fullScreenMode = FullScreenMode.ExclusiveFullScreen;
Screen.fullScreen = true;
Resolution res = Screen.currentResolution;
Screen.SetResolution(res.width, res.height, FullScreenMode.ExclusiveFullScreen, res.refreshRateRatio);
}
else if (index == 2)
{
Screen.fullScreenMode = FullScreenMode.FullScreenWindow;
Screen.fullScreen = true;
Resolution res = Screen.currentResolution;
Screen.SetResolution(res.width, res.height, FullScreenMode.FullScreenWindow, res.refreshRateRatio);
if (coroutineRunner != null)
{
coroutineRunner.StartCoroutine(ApplyWindowStyleNextFrameStatic(false));
}
}
}
catch (Exception ex)
{
Debug.LogWarning($"ApplyScreenMode failed: {ex}");
}
}
private static void ApplySavedResolutionStatic()
{
int savedResIndex = PlayerPrefs.GetInt(PrefKeyResolutionIndex, -2);
if (savedResIndex == -1)
{
int customWidth = PlayerPrefs.GetInt(PrefKeyCustomResW, -1);
int customHeight = PlayerPrefs.GetInt(PrefKeyCustomResH, -1);
if (customWidth > 0 && customHeight > 0)
{
Screen.SetResolution(customWidth, customHeight, Screen.fullScreenMode, Screen.currentResolution.refreshRateRatio);
}
return;
}
if (savedResIndex < 0)
{
return;
}
Vector2Int[] candidates =
{
new Vector2Int(1024, 576),
new Vector2Int(1152, 648),
new Vector2Int(1280, 720),
new Vector2Int(1366, 768),
new Vector2Int(1600, 900),
new Vector2Int(1920, 1080),
new Vector2Int(2560, 1440),
new Vector2Int(3440, 1440),
new Vector2Int(3200, 1800),
new Vector2Int(3840, 2160),
new Vector2Int(5120, 2880),
new Vector2Int(7680, 4320),
new Vector2Int(15360, 8640)
};
if (savedResIndex >= candidates.Length)
{
return;
}
Vector2Int resolution = candidates[savedResIndex];
Resolution currentResolution = Screen.currentResolution;
if (resolution.x > currentResolution.width || resolution.y > currentResolution.height)
{
return;
}
Screen.SetResolution(resolution.x, resolution.y, Screen.fullScreenMode, currentResolution.refreshRateRatio);
}
private static System.Collections.IEnumerator ApplyWindowStyleNextFrameStatic(bool enable)
{
yield return null;
yield return new WaitForSeconds(0.05f);
EnableWindowedModeResizableStatic(enable);
}
private static void EnableWindowedModeResizableStatic(bool enable)
{
#if UNITY_STANDALONE_WIN && !UNITY_EDITOR
try
{
IntPtr hWnd = GetForegroundWindow();
if (hWnd == IntPtr.Zero) return;
const int GWL_STYLE = -16;
const int WS_OVERLAPPEDWINDOW = unchecked((int)0x00CF0000);
const int WS_POPUP = unchecked((int)0x80000000);
if (IntPtr.Size == 8)
{
long style = GetWindowLongPtr64(hWnd, GWL_STYLE);
if (enable)
{
style &= ~((long)WS_POPUP);
style |= WS_OVERLAPPEDWINDOW;
}
else
{
style &= ~((long)WS_OVERLAPPEDWINDOW);
style |= (long)WS_POPUP;
}
SetWindowLongPtr64(hWnd, GWL_STYLE, style);
SetWindowPos(hWnd, IntPtr.Zero, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
}
else
{
int style = GetWindowLong32(hWnd, GWL_STYLE);
if (enable)
{
style &= ~WS_POPUP;
style |= WS_OVERLAPPEDWINDOW;
}
else
{
style &= ~((int)WS_OVERLAPPEDWINDOW);
style |= (int)WS_POPUP;
}
SetWindowLong32(hWnd, GWL_STYLE, style);
SetWindowPos(hWnd, IntPtr.Zero, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
}
}
catch (Exception e)
{
Debug.LogWarning($"EnableWindowedModeResizable failed: {e}");
}
#endif
}
#if UNITY_STANDALONE_WIN && !UNITY_EDITOR
[DllImport("user32.dll")]
private static extern IntPtr GetForegroundWindow();
+254 -8
View File
@@ -3,11 +3,17 @@ using UnityEngine.UI;
using System.IO;
using System.Text.RegularExpressions;
using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
public class readDeviceInfo : MonoBehaviour
{
private const string LabelColorHex = "#2457c6";
private const string ValueColorHex = "#6b8acd";
private const float ColumnPaddingPixels = 32f;
private const int MinimumGapSpaces = 2;
public Button reread_button;
public Button export_button;
public Text deviceInfo_text;
@@ -25,7 +31,9 @@ public class readDeviceInfo : MonoBehaviour
}
if (deviceInfo_text != null)
deviceInfo_text.supportRichText = true;
{
ConfigureDeviceInfoText();
}
read_deviceInfo();
}
@@ -45,7 +53,8 @@ public class readDeviceInfo : MonoBehaviour
private void read_deviceInfo()
{
StringBuilder sb = new StringBuilder();
ConfigureDeviceInfoText();
var entries = new System.Collections.Generic.List<KeyValuePair<string, string>>();
void AddEntry(string title, object value)
{
@@ -54,9 +63,7 @@ public class readDeviceInfo : MonoBehaviour
else if (value is bool b) str = b ? "是" : "否";
else str = value.ToString();
sb.AppendLine($"<b>{title}</b>");
sb.AppendLine(str);
sb.AppendLine();
entries.Add(new KeyValuePair<string, string>(title ?? string.Empty, str ?? string.Empty));
}
AddEntry("平台", Application.platform.ToString());
@@ -101,7 +108,7 @@ public class readDeviceInfo : MonoBehaviour
AddEntry("启用后台运行", Application.runInBackground);
AddEntry("启用多线程渲染", SystemInfo.graphicsMultiThreaded);
string output = sb.ToString();
string output = BuildTwoColumnOutput(entries);
if (deviceInfo_text != null)
{
@@ -125,8 +132,7 @@ public class readDeviceInfo : MonoBehaviour
try
{
string raw = deviceInfo_text.text;
// Remove <b> tags and add colon after title
string cleaned = Regex.Replace(raw, "<b>(.*?)</b>", "$1:");
string cleaned = Regex.Replace(raw, "<.*?>", string.Empty);
File.WriteAllText(path, cleaned);
}
catch (System.Exception)
@@ -134,6 +140,246 @@ public class readDeviceInfo : MonoBehaviour
}
}
private string BuildTwoColumnOutput(System.Collections.Generic.IReadOnlyList<KeyValuePair<string, string>> entries)
{
if (entries == null || entries.Count == 0)
{
return string.Empty;
}
float maxLabelWidth = 0f;
for (int i = 0; i < entries.Count; i++)
{
maxLabelWidth = Mathf.Max(maxLabelWidth, MeasurePlainTextWidth(entries[i].Key));
}
float availableWidth = GetAvailableTextWidth();
float targetLabelWidth = maxLabelWidth + ColumnPaddingPixels;
if (availableWidth > 0f)
{
targetLabelWidth = Mathf.Min(targetLabelWidth, availableWidth * 0.38f);
}
float spaceWidth = Mathf.Max(1f, MeasurePlainTextWidth(" "));
int continuationIndentSpaces = Mathf.Max(MinimumGapSpaces, Mathf.CeilToInt(targetLabelWidth / spaceWidth) + MinimumGapSpaces);
string continuationIndent = new string(' ', continuationIndentSpaces);
StringBuilder sb = new StringBuilder();
for (int i = 0; i < entries.Count; i++)
{
string label = entries[i].Key ?? string.Empty;
string value = entries[i].Value ?? string.Empty;
int gapSpaces = Mathf.Max(
MinimumGapSpaces,
Mathf.CeilToInt((targetLabelWidth - MeasurePlainTextWidth(label)) / spaceWidth) + MinimumGapSpaces);
string padding = new string(' ', gapSpaces);
float firstLineValueWidth = availableWidth > 0f
? Mathf.Max(32f, availableWidth - MeasurePlainTextWidth(label) - MeasurePlainTextWidth(padding))
: 0f;
float continuationValueWidth = availableWidth > 0f
? Mathf.Max(32f, availableWidth - MeasurePlainTextWidth(continuationIndent))
: 0f;
List<string> wrappedLines = WrapValueLines(value, firstLineValueWidth, continuationValueWidth);
sb.Append("<color=").Append(LabelColorHex).Append("><b>")
.Append(EscapeRichText(label))
.Append("</b></color>")
.Append(padding)
.Append("<color=").Append(ValueColorHex).Append(">")
.Append(EscapeRichText(wrappedLines[0]))
.Append("</color>");
for (int lineIndex = 1; lineIndex < wrappedLines.Count; lineIndex++)
{
sb.AppendLine();
sb.Append(continuationIndent)
.Append("<color=").Append(ValueColorHex).Append(">")
.Append(EscapeRichText(wrappedLines[lineIndex]))
.Append("</color>");
}
if (i < entries.Count - 1)
{
sb.AppendLine();
}
}
return sb.ToString();
}
private void ConfigureDeviceInfoText()
{
if (deviceInfo_text == null)
{
return;
}
deviceInfo_text.supportRichText = true;
deviceInfo_text.alignment = TextAnchor.UpperLeft;
deviceInfo_text.horizontalOverflow = HorizontalWrapMode.Wrap;
deviceInfo_text.verticalOverflow = VerticalWrapMode.Overflow;
deviceInfo_text.resizeTextForBestFit = false;
}
private float GetAvailableTextWidth()
{
if (deviceInfo_text == null)
{
return 0f;
}
RectTransform rectTransform = deviceInfo_text.rectTransform;
if (rectTransform == null)
{
return 0f;
}
float width = rectTransform.rect.width;
return width > 0f ? width : 0f;
}
private float MeasurePlainTextWidth(string text)
{
if (deviceInfo_text == null || string.IsNullOrEmpty(text))
{
return 0f;
}
TextGenerationSettings settings = deviceInfo_text.GetGenerationSettings(Vector2.zero);
settings.richText = false;
settings.generateOutOfBounds = true;
TextGenerator generator = new TextGenerator();
return generator.GetPreferredWidth(text, settings) / deviceInfo_text.pixelsPerUnit;
}
private List<string> WrapValueLines(string value, float firstLineWidth, float continuationLineWidth)
{
string normalized = NormalizeLineBreaks(value);
if (string.IsNullOrEmpty(normalized))
{
return new List<string> { string.Empty };
}
string[] rawLines = normalized.Split('\n');
List<string> wrapped = new List<string>();
for (int i = 0; i < rawLines.Length; i++)
{
string rawLine = rawLines[i];
List<string> lineParts = WrapSingleLine(rawLine, wrapped.Count == 0 ? firstLineWidth : continuationLineWidth);
if (lineParts.Count == 0)
{
lineParts.Add(string.Empty);
}
wrapped.Add(lineParts[0]);
for (int j = 1; j < lineParts.Count; j++)
{
wrapped.Add(lineParts[j]);
}
}
return wrapped.Count > 0 ? wrapped : new List<string> { string.Empty };
}
private List<string> WrapSingleLine(string text, float maxWidth)
{
List<string> lines = new List<string>();
if (string.IsNullOrEmpty(text) || maxWidth <= 0f)
{
lines.Add(text ?? string.Empty);
return lines;
}
string remaining = text;
while (!string.IsNullOrEmpty(remaining))
{
if (MeasurePlainTextWidth(remaining) <= maxWidth)
{
lines.Add(remaining);
break;
}
int splitIndex = FindSplitIndex(remaining, maxWidth);
if (splitIndex <= 0 || splitIndex >= remaining.Length)
{
lines.Add(remaining);
break;
}
string current = remaining.Substring(0, splitIndex).TrimEnd();
if (current.Length == 0)
{
current = remaining.Substring(0, Mathf.Min(1, remaining.Length));
splitIndex = current.Length;
}
lines.Add(current);
remaining = remaining.Substring(splitIndex).TrimStart();
}
return lines;
}
private int FindSplitIndex(string text, float maxWidth)
{
int lastBreakableIndex = -1;
int fallbackIndex = -1;
for (int i = 1; i <= text.Length; i++)
{
string candidate = text.Substring(0, i);
if (MeasurePlainTextWidth(candidate) <= maxWidth)
{
fallbackIndex = i;
if (IsBreakableCharacter(text[i - 1]))
{
lastBreakableIndex = i;
}
continue;
}
break;
}
if (lastBreakableIndex > 0)
{
return lastBreakableIndex;
}
return fallbackIndex;
}
private static bool IsBreakableCharacter(char c)
{
return char.IsWhiteSpace(c) || c == '/' || c == '\\' || c == '_' || c == '-' || c == '.' || c == ':' || c == ')' || c == ']';
}
private static string NormalizeLineBreaks(string input)
{
if (string.IsNullOrEmpty(input))
{
return string.Empty;
}
return input.Replace("\r\n", "\n").Replace('\r', '\n');
}
private static string EscapeRichText(string input)
{
if (string.IsNullOrEmpty(input))
{
return string.Empty;
}
return input.Replace("&", "&amp;")
.Replace("<", "&lt;")
.Replace(">", "&gt;");
}
string ShowSaveFileDialog()
{
#if UNITY_EDITOR
+15 -1
View File
@@ -238,7 +238,15 @@ public class userSettings : MonoBehaviour
index = 0;
}
LocalizationService.SetLanguage(_availableLanguageCodes[index]);
string targetLanguageCode = _availableLanguageCodes[index];
if (IsTemporarilyUnsupportedLanguage(targetLanguageCode))
{
gNotice.error.display(LocalizationService.LocalizeLiteral("暂不支持该语言"));
SyncLanguageDropdownValue();
return;
}
LocalizationService.SetLanguage(targetLanguageCode);
}
private void HandleLanguageChanged(string languageCode)
@@ -247,6 +255,12 @@ public class userSettings : MonoBehaviour
UpdateUserInfoDisplay();
}
private static bool IsTemporarilyUnsupportedLanguage(string languageCode)
{
return !string.IsNullOrWhiteSpace(languageCode) &&
languageCode.StartsWith("en", System.StringComparison.OrdinalIgnoreCase);
}
private void OnClearupSaveDataClicked()
{
if (clearupClickCount > 0 && Time.unscaledTime > clearupExpireTime)