目前常規的PC游戲顯示模式有幾種模式如下,

一、視窗模式(FullScreenMode.Windowed)

二、獨占模式(FullScreenMode.ExclusiveFullScreen)

FPS游戲多數使用獨占模式,也是最早的全屏幕模式,性能較好,但使用 Alt+Tab時切換會出現閃頻

三、無邊框模式 (FullScreenMode.FullScreenWindow)

目前游戲多數使用這個模式,開啓後切換不會出現閃屏


問題1、切換解析度時屏幕出現黑屏無訊號

Exclusive Fullscreen 必須使用顯示卡的「原生支援模式」(Display Modes),Windows 桌面「縮放 / 插值後」能顯示許多非原生模式,但 Unity 的 Exclusive Fullscreen 只能切換到顯示卡真正支援的模式,不能靠 OS 自動補正。

如果顯示卡沒有 真正的 1280×800 模式:
Windows 桌面會用 GPU scaling → 顯示正常

Unity Exclusive Fullscreen 嘗試切換 → 找不到完全匹配的 mode → 會改用 fallback(通常 1280×720 或 native resolution)

結果畫面偏移、拉伸或出現 左側黑屏

所以:
你以為進入了 1280×800,但實際上顯卡沒有成功切換到這個 mode。

問題2、兩個屏幕,一個 1080p,一個2k,切換2k解析度時出現錯誤,游戲無法調整到對應的2k解析度

目前沒有處理這個問題。

問題3、無邊框模式切換獨占模式無效

在主要解析度,譬如設備解析度為 1920 X 1080,情況下從無邊框模式切換到獨占模式,雖然切換成功,但實際上卻是無邊框模式,這是 windows 全螢幕最佳化處理的結果,unity無法干涉。

原因是 Windows 10/11 強制 Fullscreen Optimizations(全螢幕最佳化),導致獨占模式被轉成 Borderless Fullscreen

Windows 會把要求 Exclusive Fullscreen 的遊戲「包裝成」
➡ Borderless Fullscreen + 可變刷新率 + 全螢幕優化圖層
這樣能快速切換視窗、疊加 UI(Xbox Bar)、降低 alt-tab 黑屏,但缺點就是:

看起來像無邊框, 還是會回報給 Unity 「正在使用 Exclusive Fullscreen」, 顯示卡 API 也會回報獨佔模式成功,所以 Unity 的 log 會印出 ExclusiveFullScreen,但你看到的是 Borderless 外觀。

需要處理【關閉全螢幕最佳化】

有兩種做法:

方法一、設定 exe 檔案,關閉全螢幕最佳化。

13693-272epjzhipf.png

方法二、游戲啓動后新增注冊表關閉最佳化,如下:
public static void SetDisableFullscreenOptimization()
{
string exePath = string.Empty;

    // 獲取當前執行檔路徑 (包含執行檔名稱)
    Debug.Log("System.AppDomain.CurrentDomain.BaseDirectory: " + AppDomain.CurrentDomain.BaseDirectory);

    // 獲取執行檔所在目錄的路徑
    Debug.Log("Application.streamingAssetsPath: " + Application.streamingAssetsPath);

    // 獲取可讀寫的持久化資料夾路徑
    Debug.Log("Application.persistentDataPath: " + Application.persistentDataPath);

    try
    {
        string baseDirectory = AppDomain.CurrentDomain.BaseDirectory;
        string exeName = Application.productName + ".exe";
        exePath = Path.Combine(baseDirectory, exeName);
    }
    catch (System.Exception e)
    {
        Debug.LogError($"使用 BaseDirectory 獲取執行檔路徑時發生錯誤: {e.Message}");
        return;
    }

    if (string.IsNullOrEmpty(exePath))
    {
        Debug.LogError("無法推導執行檔路徑,操作取消。");
        return;
    }

    // 檢查檔案是否存在(額外的安全檢查)
    if (!File.Exists(exePath))
    {
        Debug.LogError($"推導的路徑找不到執行檔。路徑: {exePath}");
        return;
    }

    // Windows 註冊表路徑
    string registryPath = @"Software\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\Layers";

    try
    {
        using (RegistryKey key = Registry.CurrentUser.CreateSubKey(registryPath))
        {
            if (key != null)
            {
                object existingValue = key.GetValue(exePath);

                // 我們要寫入的值是 "~ DISABLEDXMAXIMIZEDWINDOWEDMODE"
                const string compatibilityFlag = "~ DISABLEDXMAXIMIZEDWINDOWEDMODE";

                if (existingValue == null || !existingValue.ToString().Contains("DISABLEDXMAXIMIZEDWINDOWEDMODE"))
                {
                    key.SetValue(exePath, compatibilityFlag, RegistryValueKind.String);
                    Debug.Log("已成功設定相容性:停用全螢幕最佳化。路徑: " + exePath);
                }
                else
                {
                    Debug.Log("相容性設定已存在,無需重複設定。");
                }
            }
            else
            {
                Debug.LogError("無法開啟或建立註冊表路徑:" + registryPath);
            }
        }
    }
    catch (System.Security.SecurityException e)
    {
        Debug.LogError($"設定註冊表失敗,可能是權限不足或其他系統錯誤: {e.Message}");
    }
    catch (System.Exception e)
    {
        Debug.LogError($"設定註冊表時發生未知錯誤: {e.Message}");
    }
}

問題4、獨占模式切換無邊框模式時,解析度無效問題

這個情況比較特殊,操作的流程如下:

先設定游戲變成全屏幕 800x600,再設定 無邊框模式 1920x1080,爲什麽顯示出來的解析度竟然是 800x600 無邊框模式

Exclusive Fullscreen 模式會改變桌面解析度,而 Windows(FSO)不會在你切到無邊框時自動切回。最後的做法是將 exe 的全螢幕最佳化關閉。

另外再補上一個流程就是切換解析度時執行兩次,如下:

Screen.SetResolution(width, height, fullScreenMode);
await UniTask.DelayFrame(1);
Screen.SetResolution(width, height, fullScreenMode);

問題5、獨占模式滑鼠可以超出游戲範圍

下面給了解決辦法,測試了其他方法無效,最後的做法是在切換到獨占模式時設定限制 Cursor 可移動範圍,詳細做法如下:

    void OnApplicationFocus(bool focus)
    {
        Cursor.visible = !focus;

#if UNITY_STANDALONE_WIN && !UNITY_EDITOR
        PlatformUtils.ClipCursorToScreen(Screen.fullScreenMode == FullScreenMode.ExclusiveFullScreen);
#endif
    }

    public static async UniTask<bool> SetupResolution(int solutionIdx, int fullMode)
    {
        //...
        await SetResolutionAsync(width, height, displayModeValue);
        PlatformUtils.ClipCursorToScreen(displayModeValue == FullScreenMode.ExclusiveFullScreen);
    }
    private static async UniTask SetResolutionAsync(int width, int height, FullScreenMode fullScreenMode)
    {
        Screen.SetResolution(width, height, fullScreenMode);
        await UniTask.DelayFrame(1);
        Screen.SetResolution(width, height, fullScreenMode);

        //加入超時處理,避免無限等待
        //這裡設定1秒超時
        int timeoutFrames = 60; // Assuming 60 frames per second
        int waitedFrames = 0;
        while (Screen.width != width || Screen.height != height)
        {
            await UniTask.Yield();   // 等下一幀
            waitedFrames++;
            if (waitedFrames > timeoutFrames)
            {
                Log.Warning("設定解析度超時");
                break;
            }
        }
    }
    public static void ClipCursorToScreen(bool clip)
    {
        if (clip)
        {
            RECT rect = new RECT
            {
                Left = 0,
                Top = 0,
                Right = Screen.width,
                Bottom = Screen.height
            };
            ClipCursor(ref rect);
        }
        else
        {
            ClipCursor(System.IntPtr.Zero);
        }
    }

問題6、游戲在熒幕2游玩,設定全螢幕后因爲要鎖定滑鼠在游戲範圍内,結果滑鼠被鎖定在熒幕1

需要根據目前在那個熒幕獲取對應 Rect 做設定,不然就會出錯,修改 code 如下:

    public static RECT GetCurrentMonitorRect()
    {
        IntPtr hwnd = GetActiveWindow();
        if (hwnd == IntPtr.Zero)
            throw new Exception("Cannot get Unity HWND");

        IntPtr hMonitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
        if(hMonitor == IntPtr.Zero)
            throw new Exception("MonitorFromWindow failed");

        MONITORINFO mi = new MONITORINFO();
        mi.cbSize = Marshal.SizeOf(typeof(MONITORINFO));

        if (!GetMonitorInfo(hMonitor, ref mi))
            throw new Exception("GetMonitorInfo failed");

        return mi.rcMonitor;
    }

    public static void ClipCursorToCurrentMonitor(bool enable)
    {
        if (!enable)
        {
            ClipCursor(IntPtr.Zero);
            return;
        }

        RECT rect = GetCurrentMonitorRect();
        ClipCursor(ref rect);
    }

問題7、游戲啓動的解析度會莫名其妙被設定成其他的解析度

我的游戲,解析度保留在視窗化,切換到無邊框,直接 alt+F4 關閉游戲(沒有保存設定,應該啓動回事視窗話),啓動后會先是無邊框(啓動讀取設定根據設定顯示對應解析度),又切換成視窗化 但如果切換無邊框并保存設定都不會有問題,唯獨游戲中切換無邊框立即關閉游戲,啓動會是無邊框,爲什麽?

Unity 在切換全螢幕模式(含無邊框)時,會立即刷新一組“硬體層級解析度記錄”

這個狀態會被同步到:

Windows 的 registry 裡,(不是你的 PlayerPrefs,而是引擎自己的)

因此:

你沒有保存遊戲內設定,但作業系統已經記住你最後使用的是無邊框模式,所以下次 Unity 在啟動時,就會以最後的「硬體顯示模式」啟動,而不是你的設定檔。

要修改這個問題,就是在套用解析度時就應該寫入設定檔,這也是爲什麽歐美FPS游戲的保存機制中,圖形的保存是修改當下就保存,而游戲功能選項卻是退回去才會提示保存。

問題8、在筆記本接外顯示器時切換全螢幕(獨占模式)直接卡死畫面但游戲流程還在執行

問題9、在4k顯示熒幕上出現周期性卡頓

可能原因分析

  1. 刷新率不匹配問題(最可能)
    你的4K螢幕支援多種刷新率,但在無邊框模式下可能沒有正確選擇:

獨占模式:遊戲可以強制使用 60Hz,與你的目標FPS完美匹配
無邊框模式:可能被強制使用 120Hz/180Hz/240Hz,但遊戲只能輸出60FPS
結果:螢幕每秒刷新120次,但遊戲只更新60幀 → 卡頓感
或者VSync與高刷新率衝突 → 幀時間不穩定
2. 桌面合成器(Desktop Window Manager)開銷
無邊框模式下,Windows DWM會介入進行視窗合成
4K解析度的合成開銷很大,尤其是高刷新率時
3. VSync行為差異
獨占模式:VSync直接控制顯示器
無邊框模式:VSync通過DWM,可能有額外延遲或幀時間波動

問題10、4k 桌面解析度切换到無邊框或是視窗模式,開啓 vsync 后選擇 60hz 卡頓情況比不開 vsync 嚴重,不開vsync,fps會跑到130-140。

Windows DWM(Desktop Window Manager) 合成 + 嚴格 16.67ms 幀同步,一旦錯過就掉到 33ms(30fps)或產生節奏性卡頓

64763-8fnbry4h87c.png

1)關閉vsync
2)fps不要卡 60

QualitySettings.vSyncCount = 0;
Application.targetFrameRate = 58;

無標籤

關注作者:

新增評論