Unity中窗口调整

Unity中让导出包的窗口变成自己希望设置成的分辨率,可以是全屏、自定义等。

1.targetWidth:宽分辨率。targetHeight:高分辨率

2.TestWidth:测试宽分辨率。TestHeight:测试高分辨率。

3.isEnd,勾选使用测试分辨率,不勾选使用宽分辨率。注意:在streamingassets文件夹中如果有自定义分辨率的文档则不会使用编辑器中设置的分辨率,而是文档分辨率。

4.WindowX:距离屏幕左上角(0,0)点开始的X轴向的位移偏移量。WindowY:距离屏幕左上角(0,0)点开始的Y轴向的位移偏移量。

5.Always On Top:勾选则一直位于屏幕所有的进程前,包括任务栏等。不勾选则和窗口一样会被遮挡。

6.StartDelay:启动后等待时间(秒),确保窗口创建完成

7.DragKey:想要拖动需要按住的键,需要设置

8.ShowDragHint:勾选则按住DragKey设置的键后可以点击

cs 复制代码
using System;
using System.Collections;
using System.IO;
using UnityEngine;

#if !UNITY_EDITOR
using System.Runtime.InteropServices;
#endif

/// <summary>
/// 固定尺寸置顶窗口管理器
/// 功能:设置固定分辨率,置顶显示,遮挡任务栏,屏幕不够时自动缩放
/// ⚠️ 编辑器中不会执行窗口操作,避免影响Unity编辑器界面
/// </summary>
public class FixedSizeTopmostWindow : MonoBehaviour
{
    [Header("=== 目标分辨率设置 ===")]
    [SerializeField, Tooltip("目标宽度(如:640)")]
    private int targetWidth = 1920;

    [SerializeField, Tooltip("目标高度(如:1080)")]
    private int targetHeight = 1080;

    [Header("=== 测试分辨率设置 ===")]
    [SerializeField]
    private int testWidth = 960;

    [SerializeField]
    private int testHeight = 540;

    [SerializeField, Tooltip("勾选后为测试分辨率,不勾选则为部署的目标分辨率")]
    private bool isEnd = false;

    [Header("=== 窗口位置设置 ===")]
    [SerializeField, Tooltip("窗口X坐标(0为最左侧)")]
    private int windowX = 0;

    [SerializeField, Tooltip("窗口Y坐标(0为最顶部)")]
    private int windowY = 0;

    [Header("=== 高级选项 ===")]
    [SerializeField, Tooltip("是否始终置顶(遮挡任务栏)")]
    private bool alwaysOnTop = true;

    [SerializeField, Tooltip("启动后等待时间(秒),确保窗口创建完成")]
    private float startDelay = 0.3f;

    [Header("=== 拖动设置 ===")]
    [SerializeField, Tooltip("拖动触发键(默认空格键)")]
    private KeyCode dragKey = KeyCode.Space;

    [SerializeField, Tooltip("显示拖动提示")]
    private bool showDragHint = true;
    private const string ResolutionFileName = "分辨率调整_宽比高.txt";
#if !UNITY_EDITOR
    // ===== Windows API 只在打包后的程序中编译 =====
    
    // Windows API 常量
    const int GWL_STYLE = -16;
    const int GWL_EXSTYLE = -20;
    const int WS_BORDER = 0x00800000;
    const int WS_CAPTION = 0x00C00000;
    const int WS_POPUP = unchecked((int)0x80000000);
    const int WS_EX_TOPMOST = 0x00000008;
    const uint SWP_SHOWWINDOW = 0x0040;
    const uint SWP_FRAMECHANGED = 0x0020;

    static readonly IntPtr HWND_TOPMOST = new IntPtr(-1);
    static readonly IntPtr HWND_NOTOPMOST = new IntPtr(-2);

    // Windows API 导入
    [DllImport("user32.dll")]
    private static extern IntPtr GetActiveWindow();

    [DllImport("user32.dll")]
    private static extern IntPtr FindWindow(string lpClassName, string lpWindowName);

    [DllImport("user32.dll")]
    private static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong);

    [DllImport("user32.dll")]
    private static extern int GetWindowLong(IntPtr hWnd, int nIndex);

    [DllImport("user32.dll")]
    private static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags);

    [DllImport("user32.dll")]
    private static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);

    [DllImport("user32.dll")]
    private static extern bool SetForegroundWindow(IntPtr hWnd);

    [DllImport("user32.dll")]
    private static extern bool GetCursorPos(out POINT lpPoint);

    [StructLayout(LayoutKind.Sequential)]
    public struct POINT
    {
        public int X;
        public int Y;
    }
#endif

    private int finalWidth;
    private int finalHeight;
    private bool isDragging = false;
    private Vector2 dragOffset;
    private string[] ReadGroupConfig()
    {
        string configPath = Path.Combine(Application.streamingAssetsPath, ResolutionFileName);
        try
        {
            if (!File.Exists(configPath))
            {
                Debug.LogError($"配置文件不存在!路径:{configPath},默认使用1920*1080");
                string[] str2 = new string[2];
                str2[0] = 1920.ToString();
                str2[1] = 1080.ToString();
                return null;
            }

            string fileContent = File.ReadAllText(configPath).Trim();
            string[] str = fileContent.Split(":");
            return str;

        }
        catch (Exception e)
        {
            Debug.LogError($"[播放端] 读取分组配置失败:{e.Message},默认使用组1");
            return null;
        }
    }

    void Start()
    {
#if UNITY_EDITOR
        Debug.Log("⚠️ <color=yellow>编辑器模式:窗口管理功能已禁用,不会影响Unity编辑器</color>");
        Debug.Log($"💡 打包后将应用:分辨率 {targetWidth}x{targetHeight},置顶:{alwaysOnTop}");
#else
        StartCoroutine(InitializeWindow());
#endif
    }

    IEnumerator InitializeWindow()
    {
#if UNITY_EDITOR
        yield break; // 编辑器中直接返回
#else
       if(ReadGroupConfig()!=null&&ReadGroupConfig().length==2)
       {
           //targetWidth = int.Parse(ReadGroupConfig()[0]);
           //targetHeight = int.Parse(ReadGroupConfig()[1]);
       }


   

        if (isEnd)
        {
            targetWidth = testWidth;
            targetHeight = testHeight;
        }

        // 计算最终分辨率(考虑屏幕限制)
        CalculateFinalResolution();

        // 先设置Unity分辨率为窗口模式
        Screen.SetResolution(finalWidth, finalHeight, false);
        Debug.Log($"Unity分辨率已设置为:{finalWidth} x {finalHeight}");

        // 等待Unity窗口创建
        yield return new WaitForSeconds(startDelay);

        // 应用Windows窗口设置
        ApplyWindowStyle();
#endif
    }

    /// <summary>
    /// 计算最终分辨率(处理屏幕太小的情况)
    /// </summary>
    void CalculateFinalResolution()
    {
        int screenWidth = Screen.currentResolution.width;
        int screenHeight = Screen.currentResolution.height;

        Debug.Log($"屏幕分辨率:{screenWidth} x {screenHeight}");
        Debug.Log($"目标分辨率:{targetWidth} x {targetHeight}");

        // 如果屏幕尺寸足够,直接使用目标分辨率
        if (screenWidth >= targetWidth && screenHeight >= targetHeight)
        {
            finalWidth = targetWidth;
            finalHeight = targetHeight;
            Debug.Log("屏幕尺寸足够,使用目标分辨率");
        }
        // 屏幕尺寸不够,需要缩放
        else
        {
            // 计算宽高比
            float targetRatio = (float)targetWidth / targetHeight;
            float screenRatio = (float)screenWidth / screenHeight;

            // 判断是宽度不够还是高度不够
            if (screenWidth < targetWidth || screenHeight < targetHeight)
            {
                // 按照最大的边进行缩放
                float scaleByWidth = (float)screenWidth / targetWidth;
                float scaleByHeight = (float)screenHeight / targetHeight;

                // 选择较小的缩放比例,确保窗口不会超出屏幕
                float scale = Mathf.Min(scaleByWidth, scaleByHeight);

                finalWidth = Mathf.RoundToInt(targetWidth * scale);
                finalHeight = Mathf.RoundToInt(targetHeight * scale);

                Debug.Log($"屏幕尺寸不足,自动缩放。缩放比例:{scale:F2}");
                Debug.Log($"最终分辨率:{finalWidth} x {finalHeight}");
            }
        }
    }

    /// <summary>
    /// 应用Windows窗口样式
    /// </summary>
    void ApplyWindowStyle()
    {
#if UNITY_EDITOR
        return; // 编辑器中不执行
#else
        // 获取Unity窗口句柄
        IntPtr hWnd = FindWindow(null, Application.productName);
        if (hWnd == IntPtr.Zero)
        {
            hWnd = GetActiveWindow();
        }

        if (hWnd == IntPtr.Zero)
        {
            Debug.LogError("无法找到Unity窗口句柄!");
            return;
        }

        Debug.Log("找到窗口句柄,开始应用样式...");

        // 移除边框和标题栏,设置为弹出窗口样式
        int currentStyle = GetWindowLong(hWnd, GWL_STYLE);
        int newStyle = WS_POPUP; // 无边框弹出窗口
        SetWindowLong(hWnd, GWL_STYLE, newStyle);

        // 设置为置顶窗口
        if (alwaysOnTop)
        {
            int exStyle = GetWindowLong(hWnd, GWL_EXSTYLE);
            SetWindowLong(hWnd, GWL_EXSTYLE, exStyle | WS_EX_TOPMOST);
            Debug.Log("窗口已设置为置顶");
        }

        // 设置窗口位置和大小
        IntPtr insertAfter = alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST;
        bool success = SetWindowPos(
            hWnd,
            insertAfter,
            windowX,
            windowY,
            finalWidth,
            finalHeight,
            SWP_SHOWWINDOW | SWP_FRAMECHANGED
        );

        if (success)
        {
            Debug.Log($"✅ 窗口设置成功!");
            Debug.Log($"   位置:({windowX}, {windowY})");
            Debug.Log($"   尺寸:{finalWidth} x {finalHeight}");
            Debug.Log($"   置顶:{alwaysOnTop}");

            // 确保窗口在最前
            SetForegroundWindow(hWnd);
            ShowWindow(hWnd, 5); // SW_SHOW
        }
        else
        {
            Debug.LogError("❌ 窗口设置失败!");
        }
#endif
    }

    /// <summary>
    /// 公共方法:运行时更新窗口设置
    /// </summary>
    public void UpdateWindowSettings(int width, int height, int x = 0, int y = 0)
    {
#if !UNITY_EDITOR
        targetWidth = width;
        targetHeight = height;
        windowX = x;
        windowY = y;

        StartCoroutine(InitializeWindow());
#else
        Debug.Log($"⚠️ 编辑器模式:窗口设置已记录但不会立即生效 ({width}x{height})");
#endif
    }

    void Update()
    {
        // ESC键退出程序(编辑器和打包后都有效)
        if (Input.GetKeyDown(KeyCode.Escape))
        {
            Debug.Log("按下ESC键,退出程序");
            Application.Quit();
#if UNITY_EDITOR
            UnityEditor.EditorApplication.isPlaying = false;
#endif
        }

#if !UNITY_EDITOR
        // 处理窗口拖动(只在打包后有效)
        HandleWindowDrag();

        // F11键切换置顶状态(只在打包后有效)
        if (Input.GetKeyDown(KeyCode.F11))
        {
            alwaysOnTop = !alwaysOnTop;
            Debug.Log($"置顶状态切换为:{alwaysOnTop}");
            ApplyWindowStyle();
        }
#endif
    }

    void OnApplicationFocus(bool hasFocus)
    {
#if !UNITY_EDITOR
        // 当程序获得焦点时,重新确保置顶
        if (hasFocus && alwaysOnTop)
        {
            IntPtr hWnd = FindWindow(null, Application.productName);
            if (hWnd != IntPtr.Zero)
            {
                SetWindowPos(hWnd, HWND_TOPMOST, windowX, windowY, finalWidth, finalHeight, SWP_SHOWWINDOW);
            }
        }
#endif
    }

#if !UNITY_EDITOR
    /// <summary>
    /// 处理窗口拖动逻辑
    /// </summary>
    void HandleWindowDrag()
    {
        // 按下拖动键开始拖动
        if (Input.GetKeyDown(dragKey))
        {
            if (Input.GetMouseButton(0))
            {
                // 如果已经按住鼠标左键,立即开始拖动
                StartDrag();
            }
        }

        // 按住拖动键时,点击鼠标左键开始拖动
        if (Input.GetKey(dragKey) && Input.GetMouseButtonDown(0))
        {
            StartDrag();
        }

        // 拖动过程中
        if (isDragging && Input.GetKey(dragKey) && Input.GetMouseButton(0))
        {
            DragWindow();
        }

        // 松开拖动键或鼠标左键,结束拖动
        if (isDragging && (!Input.GetKey(dragKey) || !Input.GetMouseButton(0)))
        {
            EndDrag();
        }
    }

    /// <summary>
    /// 开始拖动
    /// </summary>
    void StartDrag()
    {
        POINT cursorPos;
        if (GetCursorPos(out cursorPos))
        {
            isDragging = true;
            // 记录鼠标点击位置相对于窗口左上角的偏移
            dragOffset = new Vector2(cursorPos.X - windowX, cursorPos.Y - windowY);

            if (showDragHint)
            {
                Debug.Log("🖱️ 开始拖动窗口...");
            }
        }
    }

    /// <summary>
    /// 拖动窗口
    /// </summary>
    void DragWindow()
    {
        POINT cursorPos;
        if (GetCursorPos(out cursorPos))
        {
            // 计算新的窗口位置(鼠标位置 - 偏移量)
            int newX = cursorPos.X - (int)dragOffset.x;
            int newY = cursorPos.Y - (int)dragOffset.y;

            // 获取窗口句柄
            IntPtr hWnd = FindWindow(null, Application.productName);
            if (hWnd == IntPtr.Zero)
            {
                hWnd = GetActiveWindow();
            }

            if (hWnd != IntPtr.Zero)
            {
                // 移动窗口(允许超出屏幕)
                SetWindowPos(hWnd, HWND_TOPMOST, newX, newY, finalWidth, finalHeight, SWP_SHOWWINDOW);
            }
        }
    }

    /// <summary>
    /// 结束拖动
    /// </summary>
    void EndDrag()
    {
        if (!isDragging) return;

        isDragging = false;

        // 获取当前鼠标位置作为最终位置
        POINT cursorPos;
        if (GetCursorPos(out cursorPos))
        {
            // 更新窗口位置变量
            windowX = cursorPos.X - (int)dragOffset.x;
            windowY = cursorPos.Y - (int)dragOffset.y;

            if (showDragHint)
            {
                Debug.Log($"✅ 拖动结束,新位置:({windowX}, {windowY})");
            }

            // 重新应用窗口样式(保持原始大小)
            ApplyWindowStyle();
        }
    }
#endif

    /// <summary>
    /// GUI显示拖动提示
    /// </summary>
    void OnGUI()
    {
        if (!showDragHint) return;

#if UNITY_EDITOR
        // 编辑器中显示提示信息
        GUIStyle style = new GUIStyle(GUI.skin.label);
        style.fontSize = 16;
        style.normal.textColor = Color.yellow;
        style.alignment = TextAnchor.UpperLeft;

        GUI.Label(new Rect(10, 10, 600, 60),
            "⚠️ <b>编辑器模式</b>\n窗口管理功能已禁用,打包后生效", style);
#else
        // 打包后显示拖动提示
        GUIStyle style = new GUIStyle(GUI.skin.label);
        style.fontSize = 16;
        style.normal.textColor = isDragging ? Color.green : Color.white;
        style.alignment = TextAnchor.UpperLeft;

        string hint = isDragging
            ? $"🖱️ 拖动中... 位置: ({windowX}, {windowY})"
            : $"💡 按住 [{dragKey}] + 鼠标左键 拖动窗口";

        GUI.Label(new Rect(10, 10, 500, 30), hint, style);
#endif
    }
}
相关推荐
c#上位机3 小时前
halcon刚性变换(平移+旋转)——vector_angle_to_rigid
人工智能·计算机视觉·c#·上位机·halcon·机器视觉
小码编匠3 小时前
WPF 实现高仿 Windows 通知提示框:工业级弹窗设计与实现
后端·c#·.net
qq_428639613 小时前
虚幻基础:特效
游戏引擎·虚幻
武藤一雄5 小时前
[WPF] 万字拆解依赖属性与附加属性
前端·microsoft·c#·.net·wpf
我好喜欢你~5 小时前
.net---图表库(LiveCharts.Wpf)
c#·wpf
qq_428639615 小时前
虚幻基础:将角色的模型替换为自己的模型
游戏引擎·虚幻
helloworddm5 小时前
GrainType详解
服务器·网络·架构·c#
yngsqq5 小时前
CAD点集生成泰森多边形Voronoi图)——CAD二次开发
c#
武藤一雄6 小时前
C#:深入浅出委托(Delegate/Func/Action/Predicate)
开发语言·后端·microsoft·微软·c#·.net