[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
实现:
-
无边框
[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 -
最小化
//最小化窗口
//具体窗口参数看这 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 仓库:点我。
- 缩放
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 仓库:点我。
- 拖拽窗体
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);
-
置顶窗体(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游戏GC优化总结---protobuf-net无GC版本优化实践
Unity3d user32.dll 拖动窗口并重新获得焦点