Files
bansonic_beta_main/Assets/scripts/settings/graphicSettings.cs
T
FloatGaming eb9b6ba78a 加入了浮动小游戏功能和设置界面
运行时请手动修改运行库目录fdBrowser
2025-12-24 06:02:06 +08:00

472 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 UnityEngine;
using UnityEngine.UI;
using System.Collections.Generic;
using System;
using System.Runtime.InteropServices;
public class graphicSettings : MonoBehaviour
{
public Dropdown screenMode_Dropdown;
public Dropdown resolution_Dropdown;
public Dropdown frameRate_Dropdown;
public Image resolutionLock_Image;
private List<Vector2Int> availableResolutions = new List<Vector2Int>();
private int tempFreeResOptionIndex = -1;
private Vector2Int lastScreenSize;
void Start()
{
InitScreenModeDropdown();
InitResolutionDropdown();
InitFrameRateDropdown();
lastScreenSize = new Vector2Int(Screen.width, Screen.height);
}
void OnDestroy()
{
if (screenMode_Dropdown != null)
screenMode_Dropdown.onValueChanged.RemoveListener(OnScreenModeChanged);
if (resolution_Dropdown != null)
resolution_Dropdown.onValueChanged.RemoveListener(OnResolutionChanged);
if (frameRate_Dropdown != null)
frameRate_Dropdown.onValueChanged.RemoveListener(OnFrameRateChanged);
}
void Update()
{
if (Screen.width != lastScreenSize.x || Screen.height != lastScreenSize.y)
{
lastScreenSize.x = Screen.width;
lastScreenSize.y = Screen.height;
OnUserResolutionChanged(new Vector2Int(Screen.width, Screen.height));
}
}
private void OnUserResolutionChanged(Vector2Int newSize)
{
if (Screen.fullScreenMode != FullScreenMode.Windowed)
return;
int match = availableResolutions.FindIndex(v => v.x == newSize.x && v.y == newSize.y);
if (match >= 0)
{
RemoveTempFreeOptionIfExists();
resolution_Dropdown.SetValueWithoutNotify(match);
}
else
{
EnsureTempFreeOptionExists();
resolution_Dropdown.SetValueWithoutNotify(tempFreeResOptionIndex);
}
}
private void EnsureTempFreeOptionExists()
{
if (resolution_Dropdown == null) return;
if (tempFreeResOptionIndex >= 0) return;
var opt = new Dropdown.OptionData("自由分辨率");
resolution_Dropdown.options.Add(opt);
tempFreeResOptionIndex = resolution_Dropdown.options.Count - 1;
}
private void RemoveTempFreeOptionIfExists()
{
if (resolution_Dropdown == null) return;
if (tempFreeResOptionIndex < 0) return;
if (tempFreeResOptionIndex < resolution_Dropdown.options.Count)
resolution_Dropdown.options.RemoveAt(tempFreeResOptionIndex);
tempFreeResOptionIndex = -1;
}
private void InitScreenModeDropdown()
{
if (screenMode_Dropdown == null) return;
var options = new List<string>() { "窗口(自由窗口)", "全屏(自适应)", "无边框窗口" };
screenMode_Dropdown.ClearOptions();
screenMode_Dropdown.AddOptions(options);
int current = MapFullScreenModeToIndex(Screen.fullScreenMode);
if (PlayerPrefs.HasKey("screenMode"))
{
int saved = PlayerPrefs.GetInt("screenMode", current);
saved = Mathf.Clamp(saved, 0, options.Count - 1);
current = saved;
ApplyScreenMode(current);
}
screenMode_Dropdown.SetValueWithoutNotify(current);
screenMode_Dropdown.onValueChanged.AddListener(OnScreenModeChanged);
}
private int MapFullScreenModeToIndex(FullScreenMode mode)
{
switch (mode)
{
case FullScreenMode.Windowed:
return 0;
case FullScreenMode.ExclusiveFullScreen:
return 1;
case FullScreenMode.FullScreenWindow:
return 2;
case FullScreenMode.MaximizedWindow:
return 0;
default:
return 0;
}
}
private void OnScreenModeChanged(int index)
{
ApplyScreenMode(index);
PlayerPrefs.SetInt("screenMode", index);
PlayerPrefs.Save();
bool isWindowed = (index == 0);
if (resolutionLock_Image != null)
resolutionLock_Image.gameObject.SetActive(!isWindowed);
if (resolution_Dropdown != null)
resolution_Dropdown.interactable = isWindowed;
}
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);
}
else if (index == 1)
{
Screen.fullScreenMode = FullScreenMode.ExclusiveFullScreen;
Screen.fullScreen = true;
Resolution res = Screen.currentResolution;
Screen.SetResolution(res.width, res.height, FullScreenMode.ExclusiveFullScreen, res.refreshRate);
}
else if (index == 2)
{
Screen.fullScreenMode = FullScreenMode.FullScreenWindow;
Screen.fullScreen = true;
Resolution res = Screen.currentResolution;
Screen.SetResolution(res.width, res.height, FullScreenMode.FullScreenWindow, res.refreshRate);
StopAllCoroutines();
StartCoroutine(ApplyWindowStyleNextFrame(false));
}
}
catch (System.Exception ex)
{
Debug.LogWarning($"ApplyScreenMode failed: {ex}");
}
}
private System.Collections.IEnumerator ApplyWindowStyleNextFrame(bool enable)
{
yield return null;
yield return new WaitForSeconds(0.05f);
EnableWindowedModeResizable(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 &= ~WS_OVERLAPPEDWINDOW;
style |= 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
}
private void InitResolutionDropdown()
{
if (resolution_Dropdown == null) return;
var candidates = new List<Vector2Int>() {
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)
};
int maxW = Screen.currentResolution.width;
int maxH = Screen.currentResolution.height;
const int MIN_W = 1024;
const int MIN_H = 720;
const int MAX_DIM = 16384;
availableResolutions.Clear();
var options = new List<string>();
foreach (var c in candidates)
{
if (c.x < MIN_W || c.y < MIN_H) continue;
if (c.x > MAX_DIM || c.y > MAX_DIM) continue;
if (c.x > maxW || c.y > maxH) continue;
availableResolutions.Add(c);
}
if (availableResolutions.Count == 0)
{
var cur = new Vector2Int(Screen.width, Screen.height);
if (cur.x >= MIN_W && cur.y >= MIN_H)
availableResolutions.Add(cur);
}
foreach (var r in availableResolutions)
{
options.Add($"{r.x} × {r.y}");
}
resolution_Dropdown.ClearOptions();
resolution_Dropdown.AddOptions(options);
int selected = 0;
for (int i = 0; i < availableResolutions.Count; i++)
{
if (availableResolutions[i].x == Screen.width && availableResolutions[i].y == Screen.height)
{
selected = i;
break;
}
}
int savedResIndex = PlayerPrefs.GetInt("resolutionIndex", -2);
if (savedResIndex >= 0 && savedResIndex < availableResolutions.Count)
{
resolution_Dropdown.SetValueWithoutNotify(savedResIndex);
var sav = availableResolutions[savedResIndex];
Screen.SetResolution(sav.x, sav.y, Screen.fullScreenMode, Screen.currentResolution.refreshRate);
RemoveTempFreeOptionIfExists();
}
else if (savedResIndex == -1)
{
int cw = PlayerPrefs.GetInt("customResW", -1);
int ch = PlayerPrefs.GetInt("customResH", -1);
if (cw > 0 && ch > 0)
{
Screen.SetResolution(cw, ch, Screen.fullScreenMode, Screen.currentResolution.refreshRate);
EnsureTempFreeOptionExists();
resolution_Dropdown.options[tempFreeResOptionIndex].text = $"{cw} × {ch}";
resolution_Dropdown.SetValueWithoutNotify(tempFreeResOptionIndex);
}
else
{
resolution_Dropdown.SetValueWithoutNotify(selected);
}
}
else
{
resolution_Dropdown.SetValueWithoutNotify(selected);
}
resolution_Dropdown.onValueChanged.AddListener(OnResolutionChanged);
int screenMode = PlayerPrefs.GetInt("screenMode", MapFullScreenModeToIndex(Screen.fullScreenMode));
bool isWindowed = (screenMode == 0) || (Screen.fullScreenMode == FullScreenMode.Windowed);
if (resolutionLock_Image != null)
resolutionLock_Image.gameObject.SetActive(!isWindowed);
resolution_Dropdown.interactable = isWindowed;
}
private void SyncResolutionDropdownToCurrent()
{
if (resolution_Dropdown == null) return;
int idx = availableResolutions.FindIndex(v => v.x == Screen.width && v.y == Screen.height);
if (idx >= 0)
{
RemoveTempFreeOptionIfExists();
resolution_Dropdown.SetValueWithoutNotify(idx);
}
else
{
EnsureTempFreeOptionExists();
resolution_Dropdown.options[tempFreeResOptionIndex].text = $"{Screen.width} × {Screen.height}";
resolution_Dropdown.SetValueWithoutNotify(tempFreeResOptionIndex);
}
}
private void OnResolutionChanged(int index)
{
if (index < 0) return;
if (index >= availableResolutions.Count)
{
PlayerPrefs.SetInt("resolutionIndex", -1);
PlayerPrefs.SetInt("customResW", Screen.width);
PlayerPrefs.SetInt("customResH", Screen.height);
PlayerPrefs.Save();
return;
}
if (index >= availableResolutions.Count) return;
var r = availableResolutions[index];
FullScreenMode mode = Screen.fullScreenMode;
int refresh = Screen.currentResolution.refreshRate;
Screen.SetResolution(r.x, r.y, mode, refresh);
PlayerPrefs.SetInt("resolutionIndex", index);
PlayerPrefs.Save();
RemoveTempFreeOptionIfExists();
if (mode == FullScreenMode.Windowed)
{
StopAllCoroutines();
StartCoroutine(ApplyWindowStyleNextFrame(true));
}
}
private void InitFrameRateDropdown()
{
if (frameRate_Dropdown == null) return;
var fpsOptions = new List<string>() { "24", "30", "60", "90", "120", "144", "165", "210", "240", "300", "无限制", "垂直同步" };
frameRate_Dropdown.ClearOptions();
frameRate_Dropdown.AddOptions(fpsOptions);
// Determine current setting
int saved = PlayerPrefs.GetInt("frameRateIndex", -999);
int selectIndex = 2; // default to 60
if (saved != -999 && saved >= 0 && saved < fpsOptions.Count)
{
selectIndex = saved;
}
else
{
if (QualitySettings.vSyncCount > 0)
selectIndex = fpsOptions.Count - 1; // VSync index
else
{
int current = Application.targetFrameRate;
if (current <= 0)
selectIndex = fpsOptions.Count - 2; // unlimited
else
{
// find nearest match
int[] candidates = new int[] { 24, 30, 60, 90, 120, 144, 165, 210, 240, 300 };
int best = 0; int bestDiff = int.MaxValue;
for (int i = 0; i < candidates.Length; i++)
{
int d = Math.Abs(candidates[i] - current);
if (d < bestDiff) { bestDiff = d; best = i; }
}
selectIndex = best;
}
}
}
frameRate_Dropdown.SetValueWithoutNotify(selectIndex);
frameRate_Dropdown.onValueChanged.AddListener(OnFrameRateChanged);
// apply selection
ApplyFrameRateByIndex(selectIndex);
}
private void OnFrameRateChanged(int index)
{
ApplyFrameRateByIndex(index);
PlayerPrefs.SetInt("frameRateIndex", index);
PlayerPrefs.Save();
}
private void ApplyFrameRateByIndex(int index)
{
if (index < 0) return;
int[] fpsValues = new int[] { 24, 30, 60, 90, 120, 144, 165, 210, 240, 300 };
if (index < fpsValues.Length)
{
QualitySettings.vSyncCount = 0;
Application.targetFrameRate = fpsValues[index];
}
else if (index == fpsValues.Length)
{
// 无限制
QualitySettings.vSyncCount = 0;
Application.targetFrameRate = -1; // unlimited / platform default
}
else if (index == fpsValues.Length + 1)
{
// 垂直同步
QualitySettings.vSyncCount = 1; // enable vsync
Application.targetFrameRate = -1;
}
}
#if UNITY_STANDALONE_WIN && !UNITY_EDITOR
[DllImport("user32.dll")]
private static extern IntPtr GetForegroundWindow();
[DllImport("user32.dll", EntryPoint = "GetWindowLong")]
private static extern int GetWindowLong32(IntPtr hWnd, int nIndex);
[DllImport("user32.dll", EntryPoint = "SetWindowLong")]
private static extern int SetWindowLong32(IntPtr hWnd, int nIndex, int dwNewLong);
[DllImport("user32.dll", EntryPoint = "GetWindowLongPtr")]
private static extern long GetWindowLongPtr64(IntPtr hWnd, int nIndex);
[DllImport("user32.dll", EntryPoint = "SetWindowLongPtr")]
private static extern long SetWindowLongPtr64(IntPtr hWnd, int nIndex, long dwNewLong);
[DllImport("user32.dll", SetLastError = true)]
private static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags);
private const uint SWP_NOSIZE = 0x0001;
private const uint SWP_NOMOVE = 0x0002;
private const uint SWP_NOZORDER = 0x0004;
private const uint SWP_FRAMECHANGED = 0x0020;
#endif
}