跳到主要內容

unity 在 STANDALONE 下的解析度問題集合

目前常規的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×720native 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 檔案,關閉全螢幕最佳化。


方法二、游戲啓動后新增注冊表關閉最佳化,如下:

    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

  • Unity 的 prefs(不是你的 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)或產生節奏性卡頓

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

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


留言

這個網誌中的熱門文章

comfyUI 運行出錯 FATAL: kernel fmha_cutlassF_f32_aligned_64x64_rf_sm80 is for sm80-sm100, but was built for sm37

在公司的圖形機器上運行 ComfyUI + WAN 時出現下面問題   FATAL: kernel fmha_cutlassF_f32_aligned_64x64_rf_sm80 is for sm80-sm100, but was built for sm37 算圖機使用的 torch 版本如下 torch 2.7.1+cu118 torchaudio 2.7.1+cu118 torchsde 0.2.6 torchvision 0.22.1+cu118 檢查 cuda 版本 GeForce RTX 5090 CUDA Version: 13.0 從官網看 RTX 5090 支持 12.0 版本 CUDA 這是因爲本地的 pytorch 版本太舊 到官網查詢版本 https://pytorch.org/get-started/locally/ $ pip3 install torch torchvision --index-url https://download.pytorch.org/whl/cu126 查詢目前版本 $ pip list | findstr torch 使用這個版本后出現下面問題 後面查詢 no kernel image 問題其實還是 pytorch 並不支持 sm_120( Blackwell架構) 查看后發現并沒有支持 sm_120 CUDA architectures : [' sm_37 ', 'sm_50', 'sm_60', 'sm_61', 'sm_70', 'sm_75', 'sm_80', 'sm_86', 'sm_90', 'compute_37'] sm_50, sm_52, sm_53 : Maxwell (GTX 900 系列) sm_60, sm_61, sm_62 : Pascal (GTX 1000, P100) sm_70, sm_72, sm_75 : Volta/Turing (V100, RTX ...
仔細回想最近的作的 stripping level,還是回頭來說說 import dll 這回事吧;因為從事遊戲改版所以很多事情是言不由衷的,某天發現 server 發送給 client 封包是 big5 encode,第一時間覺得為何不用 unicode 呢?但仔細想也許是遊戲剛規劃時 DB 都已經存了 big5 編碼字串可能已經改不動了,想想也是就算了,結果一包到 android 就發生了 GetEncoding error,因為 android 是採用 unicode 編碼的,在網路上找了找終於找到解法,可以參考底下網址的討論串,主要是下載  I18N.dll、I18N.CJK.dll、I18N.West.dll 並丟到 Assets 資料夾下就可以了,當時還不是很了解為什麼只知道把 dll 都丟進去在手機上就可以跑,直到使用 stripping level 後整個又掛了,才認真去 trace, 問題在於勾選 strip asm 包檔後 I18N.CJK 就沒被包到 apk 裡面,而要解決 strip asm(~"~ 沒辦法 strip asm 很吸引我,一定要想辦法克服)採用了 link.xml 的方式,但這樣要怎麼知道弄進哪些 lib 呢?

熟悉 Unity Memory

    打開始跟 app memory 奮戰,摸索了好一陣子,不管是跑在 andorid 還是 Ios,之前做 WebPlayer 的經驗都派不上用場,也好在網路上資源很多,只要不恥繁找就能找到你所想要的,在此分享的主要是 android(ps. ~"~ 手頭上只有兩台 android 設備)。