[Unity 3d] 使用 Unity 开发无边框可拖拽缩放置顶最小化的应用

Posted weixin_40012419

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[Unity 3d] 使用 Unity 开发无边框可拖拽缩放置顶最小化的应用相关的知识,希望对你有一定的参考价值。

文章出处:
https://www.jianshu.com/p/c81342e96def

using System;
using System.Runtime.InteropServices;
using System.Text;
using UnityEngine;

public static class PInvoke

    static IntPtr ptr;
    public static IntPtr UnityHWnd
    
        get
        
            if (ptr == null || ptr == IntPtr.Zero)
            
                ptr = GetUnityWindow();
            
            return ptr;
        
    

    #region 常量
    //https://docs.microsoft.com/zh-cn/windows/win32/winmsg/window-styles
    public const ulong WS_MAXIMIZEBOX = 0x00010000L; //最大化的按钮禁用
    public const ulong WS_DLGFRAME = 0x00400000L; //不现实边框
    public const ulong WS_SIZEBOX = 0x00040000L; //调大小的边框
    public const ulong WS_BORDER = 0x00800000L; //边框
    public const ulong WS_CAPTION = 0x00C00000L; //标题栏

    // Retreives pointer to WindowProc function.
    public const int GWLP_WNDPROC = -4; //Windows 绘制方法的指针
    public const int WM_SIZING = 0x214;
    public const int WS_POPUP = 0x800000;
    public const int GWL_STYLE = -16;
    //边框参数
    public const uint SWP_SHOWWINDOW = 0x0040;
    public const uint SWP_NOMOVE = 0x0002;
    public const int SW_SHOWMINIMIZED = 2;//(最小化窗口)
    // Name of the Unity window class used to find the window handle.
    public const string UNITY_WND_CLASSNAME = "UnityWndClass";
    #endregion

    #region Win32 API
    // Passes message information to the specified window procedure.
    [DllImport("user32.dll")]
    public static extern IntPtr CallWindowProc(IntPtr lpPrevWndFunc, IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);

    //获得窗口样式
    [DllImport("user32.dll", EntryPoint = "GetWindowLongPtr")]
    public static extern IntPtr GetWindowLongPtr(IntPtr hwnd, int nIndex);
    // Retrieves the dimensions of the bounding rectangle of the specified window.
    // The dimensions are given in screen coordinates that are relative to the upper-left corner of the screen.
    [DllImport("user32.dll", SetLastError = true)]
    public static extern bool GetWindowRect(IntPtr hwnd, ref RECT lpRect);
    // Retrieves the coordinates of a window's client area. The client coordinates specify the upper-left
    // and lower-right corners of the client area. Because client coordinates are relative to the upper-left
    // corner of a window's client area, the coordinates of the upper-left corner are (0,0).
    [DllImport("user32.dll")]
    public static extern bool GetClientRect(IntPtr hWnd, ref RECT lpRect);

    // 改变指定窗口的属性 ,该函数还在额外窗口内存中的指定偏移处设置一个值。
    [DllImport("user32.dll", EntryPoint = "SetWindowLongPtr", CharSet = CharSet.Auto)]
    public static extern IntPtr SetWindowLongPtr(IntPtr hWnd, int nIndex, IntPtr dwNewLong);

    //设置当前窗口的显示状态
    [DllImport("user32.dll")]
    public static extern bool ShowWindow(System.IntPtr hwnd, int nCmdShow);

    //设置窗口位置,大小
    [DllImport("user32.dll")]
    public static extern bool SetWindowPos(IntPtr hWnd, int hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags);

    // 通过将每个窗口的句柄依次传递给应用程序定义的回调函数,枚举与线程关联的所有非子窗口。
    [DllImport("user32.dll")]
    private static extern bool EnumThreadWindows(uint dwThreadId, EnumWindowsProc lpEnumFunc, IntPtr lParam);
    private delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam);
    //检索调用线程的线程标识符。
    [DllImport("kernel32.dll")]
    private static extern uint GetCurrentThreadId();
    // 检索指定窗口所属的类的名称。
    [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    private static extern int GetClassName(IntPtr hWnd, StringBuilder lpString, int nMaxCount);


    //窗口拖动
    [DllImport("user32.dll")]
    public static extern bool ReleaseCapture();
    [DllImport("user32.dll")]
    public static extern bool SendMessage(IntPtr hwnd, int wMsg, int wParam, int lParam);


    #endregion
    #region Static Function
    //最小化窗口
    //具体窗口参数看这     https://msdn.microsoft.com/en-us/library/windows/desktop/ms633548(v=vs.85).aspx
    public static void SetMinWindows()
    
        if (!Application.isEditor)
        
            ShowWindow(UnityHWnd, SW_SHOWMINIMIZED);
        
    

    //拖动窗口
    public static void DragWindow()
    
        ReleaseCapture();
        SendMessage(UnityHWnd, 0xA1, 0x02, 0);
        SendMessage(UnityHWnd, 0x0202, 0, 0);
    

    public static IntPtr GetUnityWindow()
    
        var unityHWnd = IntPtr.Zero;
        EnumThreadWindows(GetCurrentThreadId(), (hWnd, lParam) =>
        
            var classText = new StringBuilder(UNITY_WND_CLASSNAME.Length + 1);
            GetClassName(hWnd, classText, classText.Capacity);

            if (classText.ToString() == UNITY_WND_CLASSNAME)
            
                unityHWnd = hWnd;
                return false;
            
            return true;
        , IntPtr.Zero);
        return unityHWnd;
    

    #endregion
    #region Assistant
    /// <summary>
    /// WinAPI RECT definition.
    /// </summary>
    [StructLayout(LayoutKind.Sequential)]
    public struct RECT
    
        public int Left;
        public int Top;
        public int Right;
        public int Bottom;
        public override string ToString()
        
            return "left = Left\\nright = Right\\ntop = Top\\nbottom = Bottom";
        
    
    #endregion

实现:

  1. 无边框

    [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSplashScreen)]
    static void InitAppWindow()

    if (Application.isEditor) return;
    var dwStyles = GetWindowLongPtr(UnityHWnd, GWL_STYLE);
    var sty = ((ulong)dwStyles);
    sty &= ~(WS_CAPTION| WS_DLGFRAME)&WS_POPUP;
    SetWindowLongPtr(UnityHWnd, GWL_STYLE, (IntPtr)sty);

    注意:设置无边框后,任务栏点击无法实现 App 最小化,如果有解决方案欢迎提 PR

  2. 最小化

    //最小化窗口
    //具体窗口参数看这 https://msdn.microsoft.com/en-us/library/windows/desktop/ms633548(v=vs.85).aspx
    public static void SetMinWindows()

    if (!Application.isEditor)

    ShowWindow(UnityHWnd, SW_SHOWMINIMIZED);


    //设置当前窗口的显示状态
    [DllImport(“user32.dll”)]
    public static extern bool ShowWindow(System.IntPtr hwnd, int nCmdShow);

Tips: 代码中用到的 win32 api 以及常量请参阅本文配套 GitHub 仓库:点我。

  1. 缩放

using UnityEngine;
using UnityEngine.EventSystems;
using static PInvoke;

public class WindowResizeHandler : MonoBehaviour, IPointerEnterHandler, IPointerExitHandler, IBeginDragHandler, IEndDragHandler, IDragHandler

bool isDragging = false;
bool isInsideOfHandler = false;
public Vector2 hotspot = Vector2.zero;

// Minimum and maximum values for window width/height in pixel.
[SerializeField]
private int minWidthPixel = 768;
[SerializeField]
private int maxWidthPixel = 2048;

public Texture2D wnes;
private float aspect = 16 / 9f;
void IBeginDragHandler.OnBeginDrag(PointerEventData eventData) => isDragging = eventData.pointerId==-1;
void IDragHandler.OnDrag(PointerEventData eventData) => WindowProcess(eventData);
void IEndDragHandler.OnEndDrag(PointerEventData eventData)

    isDragging = false;
    if (!isInsideOfHandler)
    
        Cursor.SetCursor(default, default, CursorMode.Auto);
    

void IPointerEnterHandler.OnPointerEnter(PointerEventData eventData)

    isInsideOfHandler = true;
    Cursor.SetCursor(wnes, hotspot, CursorMode.Auto);

void IPointerExitHandler.OnPointerExit(PointerEventData eventData)

    isInsideOfHandler = false;
    if (!isDragging)
    
        Cursor.SetCursor(default, default, CursorMode.Auto);
    

private void WindowProcess(PointerEventData eventData)

    if (Application.isEditor || eventData.pointerId != -1) return;
    RECT rc = default;
    GetWindowRect(UnityHWnd, ref rc);
    int newWidth = Mathf.Clamp(rc.Right - rc.Left + Mathf.RoundToInt(eventData.delta.x), minWidthPixel, maxWidthPixel);
    int newHeight = Mathf.RoundToInt(newWidth / aspect);
    SetWindowPos(UnityHWnd, 0, rc.Left, rc.Top, newWidth, newHeight, SWP_SHOWWINDOW);

Tips: 代码中用到的 win32 api 以及常量请参阅本文配套 GitHub 仓库:点我。

  1. 拖拽窗体

using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
using static PInvoke;
[RequireComponent(typeof(Graphic))]
public class WindowMoveHandler : MonoBehaviour,IPointerDownHandler,IPointerUpHandler,IPointerExitHandler

static bool isDrag = false;
void IPointerDownHandler.OnPointerDown(PointerEventData eventData) => isDrag = eventData.pointerId==-1;
void IPointerExitHandler.OnPointerExit(PointerEventData eventData) => isDrag = false;
void IPointerUpHandler.OnPointerUp(PointerEventData eventData) => isDrag = !(eventData.pointerId==-1);
private void Update()

if (!Application.isEditor&&isDrag)

DragWindow();


//拖动窗口
public static void DragWindow()

    ReleaseCapture();
    SendMessage(UnityHWnd, 0xA1, 0x02, 0);
    SendMessage(UnityHWnd, 0x0202, 0, 0);

  1. 置顶窗体(Topmost)

    // 应用窗口置顶
    public static void SetTopmost(bool isTopmost)

    if (!Application.isEditor)

    int ptr = isTopmost ? -1 : -2;
    SetWindowPos(UnityHWnd, ptr, 0, 0, 0, 0, 1 | 2 | 64);//0x0040

    else

    Debug.LogWarning($“nameof(PInvoke): 为避免编辑器行为异常,请打包 exe 后测试!”);


    Tips: 代码中用到的 win32 api 以及常量请参阅本文配套 GitHub 仓库:点我。

扩展阅读:
System-Tray-Icon-For-Unity - 为 Unity 开发的 App 提供 System Tray Icon,后面计划使用 Unity MenuItem 的方式,现在使用起来感觉不怎么便利。
UnitySkipSplash - 几句话跳过 Unity Logo 闪屏界面,别问我为何这么 big 胆,仅供学习用途嘛。
Simple-Customize-ERP-System - 本文配套的 GitHub 仓库。
写到最后:
版权所有,转载请注明出处!

作者:雨落随风
链接:https://www.jianshu.com/p/c81342e96def
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

WPF 窗口去除顶部边框(正宗无边框)

原文:WPF 窗口去除顶部边框(正宗无边框)

最近在做一个大屏展示视频图片的项目,功能并不复杂,半天的工作量吧,一开始同事采用的Unity3D进行开发,但是里面要播放4K视频,Unity 的短板就是视频的播放了,今晚就要交付了,我一早就来公司,决定用WPF重新开发一版,各项功能都好了,唯独顶部总是显示一条白色的边,已经设置WindowStyle为None了也没用,幸得网上大神提供的资料,终于解决了这个小问题。

XAML内容如下:

<Window x:Class="WPF_VideoPlayer.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WPF_VideoPlayer"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="2880" Loaded="Window_Loaded" WindowChrome.WindowChrome="{DynamicResource WindowChromeKey}" AllowsTransparency="True" WindowStyle="None" ResizeMode="NoResize" WindowState="Maximized" Topmost="True" Background="Black">

    <Window.Resources>
        <WindowChrome x:Key="WindowChromeKey">
            <WindowChrome.ResizeBorderThickness>
                <Thickness>0</Thickness>
            </WindowChrome.ResizeBorderThickness>
        </WindowChrome>
    </Window.Resources>

    <Grid>
    </Grid>
</Window>

以上是关于[Unity 3d] 使用 Unity 开发无边框可拖拽缩放置顶最小化的应用的主要内容,如果未能解决你的问题,请参考以下文章

在 Unity3D 中更改按钮的边框颜色

Unity3D游戏GC优化总结---protobuf-net无GC版本优化实践

Unity3d user32.dll 拖动窗口并重新获得焦点

Unity、Torque3D、Google O3D、WebGl....选择哪个? [关闭]

unity3d 怎样发布android程序

游戏开发工具unity4.X与unity 3D有啥区别?