Skip to content

Unity 实现透明背景桌面

以下代码实测于 unity2020.3 / Windows 10 系统

步骤:

将以下脚本命名为TransparentWindow添加到Camera

效果

不带操作栏,鼠标点击不可穿透

第一

csharp
using System;
using System.Runtime.InteropServices;
using UnityEngine;

public class TransparentWindow : MonoBehaviour
{
    struct MARGINS
    {
        public int cxLeftWidth;
        public int cxRightWidth;
        public int cyTopHeight;
        public int cyBottomHeight;
    }

    #region 定义外部函数

    // 获取显示在最上面的窗口
    [DllImport("user32.dll")]
    static extern IntPtr GetForegroundWindow();

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

    // 注意要编写与 32 位和 64 位版本的 Windows 兼容的代码 请使用SetWindowLongPtr
    [DllImport("user32.dll")]
    private static extern int SetWindowLong(IntPtr hWnd, int nIndex, uint dwNewLong);

    [DllImport("Dwmapi.dll")]
    private static extern uint DwmExtendFrameIntoClientArea(IntPtr hWnd, ref MARGINS margins);

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

    [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

    // 窗口句柄
    IntPtr Handle;

    void Start()
    {
#if !UNITY_EDITOR

        // 获取窗口句柄
        Handle = GetForegroundWindow();

        // 设置窗口的属性
        SetWindowLong(Handle, -16, 0x80000000);

        var margins = new MARGINS() { cxLeftWidth = -1 };

        // 将窗口框架扩展到工作区
        DwmExtendFrameIntoClientArea(Handle, ref margins);

        // 设置窗口位置
        SetWindowPos(Handle, -1, 0, 0, 0, 0, 2 | 1 | 64);
#endif
    }


    void Update()
    {
#if !UNITY_EDITOR
        // 保持窗口始终在最前面
        if (Handle != GetForegroundWindow()) SetForegroundWindow(Handle);
        // 左键拖动
        if (Input.GetMouseButtonDown(0))
        {
            ReleaseCapture();
            SendMessage(Handle, 0xA1, 0x02, 0);
            SendMessage(Handle, 0x0202, 0, 0);
        }
#endif
    }
}

第二

以下这段代码的效果是半透明的感觉,而且游戏对象也半透明了。鼠标可点击

csharp
//Note: Inspired by and uses some code found here: http://forum.unity3d.com/threads/windows-api-calls.127719/

using UnityEngine;
using System.Collections;
using System;
using System.Runtime.InteropServices; // Pro and Free!!!

//WARNING!! Running this code inside Unity when not in a build will make the entire development environment transparent
//Restarting Unity would however resolve

public class TransparentWindow : MonoBehaviour
{
    [DllImport("user32.dll", EntryPoint = "SetWindowLongA")]
    static extern int SetWindowLong(int hwnd, int nIndex, long dwNewLong);
    [DllImport("user32.dll")]
    static extern bool ShowWindowAsync(int hWnd, int nCmdShow);
    [DllImport("user32.dll", EntryPoint = "SetLayeredWindowAttributes")]
    static extern int SetLayeredWindowAttributes(int hwnd, int crKey, byte bAlpha, int dwFlags);
    [DllImport("user32.dll", EntryPoint = "GetActiveWindow")]
    private static extern int GetActiveWindow();
    [DllImport("user32.dll", EntryPoint = "GetWindowLong")]
    private static extern long GetWindowLong(int hwnd, int nIndex);
    [DllImport("user32.dll", EntryPoint = "SetWindowPos")]
    private static extern int SetWindowPos(int hwnd, int hwndInsertAfter, int x, int y, int cx, int cy, int uFlags);

    void Start()
    {
        int handle = GetActiveWindow();
        int fWidth = Screen.width;
        int fHeight = Screen.height;

        //Remove title bar
        long lCurStyle = GetWindowLong(handle, -16);     // GWL_STYLE=-16
        int a = 12582912;   //WS_CAPTION = 0x00C00000L
        int b = 1048576;    //WS_HSCROLL = 0x00100000L
        int c = 2097152;    //WS_VSCROLL = 0x00200000L
        int d = 524288;     //WS_SYSMENU = 0x00080000L
        int e = 16777216;   //WS_MAXIMIZE = 0x01000000L

        lCurStyle &= ~(a | b | c | d);
        lCurStyle &= e;
        SetWindowLong(handle, -16, lCurStyle);// GWL_STYLE=-16

        // Transparent windows with click through
        SetWindowLong(handle, -20, 524288 | 32);//GWL_EXSTYLE=-20; WS_EX_LAYERED=524288=&h80000, WS_EX_TRANSPARENT=32=0x00000020L
        SetLayeredWindowAttributes(handle, 0, 51, 2);// Transparency=51=20%, LWA_ALPHA=2

        SetWindowPos(handle, 0, 0, 0, fWidth, fHeight, 32 | 64); //SWP_FRAMECHANGED = 0x0020 (32); //SWP_SHOWWINDOW = 0x0040 (64)
        ShowWindowAsync(handle, 3); //Forces window to show in case of unresponsive app    // SW_SHOWMAXIMIZED(3)
    }
}

第三

可透明 可鼠标穿透

csharp

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

public class TransparentWindow : MonoBehaviour
{
    [SerializeField]
    private Material m_Material;

    private struct MARGINS
    {
        public int cxLeftWidth;
        public int cxRightWidth;
        public int cyTopHeight;
        public int cyBottomHeight;
    }

    [DllImport("user32.dll")]
    private static extern IntPtr GetActiveWindow();

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

    [DllImport("Dwmapi.dll")]
    private static extern uint DwmExtendFrameIntoClientArea(IntPtr hWnd, ref MARGINS margins);

    [DllImport("user32.dll", EntryPoint = "SetWindowPos")]
    private static extern int SetWindowPos(IntPtr hwnd, IntPtr hwndInsertAfter, int x, int y, int cx, int cy, int uFlags);

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

    [DllImport("user32.dll", EntryPoint = "SetLayeredWindowAttributes")]
    static extern int SetLayeredWindowAttributes(IntPtr hwnd, int crKey, byte bAlpha, int dwFlags);

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

    const int GWL_STYLE = -16;
    const int GWL_EXSTYLE = -20;
    const uint WS_POPUP = 0x80000000;
    const uint WS_VISIBLE = 0x10000000;

    const uint WS_EX_TOPMOST = 0x00000008;
    const uint WS_EX_LAYERED = 0x00080000;
    const uint WS_EX_TRANSPARENT = 0x00000020;

    const int SWP_FRAMECHANGED = 0x0020;
    const int SWP_SHOWWINDOW = 0x0040;
    const int LWA_ALPHA = 2;

    private IntPtr HWND_TOPMOST = new IntPtr(-1);

    private IntPtr _hwnd;

    void Start()
    {
#if !UNITY_EDITOR
    MARGINS margins = new MARGINS() { cxLeftWidth = -1 };
    _hwnd = GetActiveWindow();
    int fWidth = Screen.width;
    int fHeight = Screen.height;

        SetWindowLong(_hwnd, GWL_STYLE, WS_POPUP | WS_VISIBLE);
    SetWindowLong(_hwnd, GWL_EXSTYLE, WS_EX_TOPMOST | WS_EX_LAYERED | WS_EX_TRANSPARENT);
        DwmExtendFrameIntoClientArea(_hwnd, ref margins);
        SetWindowPos(_hwnd, HWND_TOPMOST, 0, 0, fWidth, fHeight, SWP_FRAMECHANGED | SWP_SHOWWINDOW); 
        ShowWindowAsync(_hwnd, 3); //Forces window to show in case of unresponsive app    // SW_SHOWMAXIMIZED(3)
#endif
    }

    void OnRenderImage(RenderTexture from, RenderTexture to)
    {
        Graphics.Blit(from, to, m_Material);
    }
}

创建一个shader

csharp
Shader "Custom/MakeTransparent" {
  Properties {
    _MainTex ("Base (RGB)", 2D) = "white" {}
    _TransparentColorKey ("Transparent Color Key", Color) = (0,1,0,1)
    _TransparencyMargin ("Transparency Margin", Float) = 0.01 
  }
  SubShader {
    Pass {
      Tags { "RenderType"="Opaque" }
      LOD 200
    
      CGPROGRAM

      #pragma vertex VertexShaderFunction
      #pragma fragment PixelShaderFunction
    
      #include "UnityCG.cginc"

      struct VertexData
      {
        float4 position : POSITION;
        float2 uv : TEXCOORD0;
      };

      struct VertexToPixelData
      {
        float4 position : SV_POSITION;
        float2 uv : TEXCOORD0;
      };

      VertexToPixelData VertexShaderFunction(VertexData input)
      {
        VertexToPixelData output;
        output.position = UnityObjectToClipPos (input.position);
        output.uv = input.uv;
        return output;
      }
    
      sampler2D _MainTex;
      float3 _TransparentColorKey;
      float _TransparencyMargin;

      float4 PixelShaderFunction(VertexToPixelData input) : SV_Target
      {
        float4 color = tex2D(_MainTex, input.uv);
      
        float deltaR = abs(color.r - _TransparentColorKey.r);
        float deltaG = abs(color.g - _TransparentColorKey.g);
        float deltaB = abs(color.b - _TransparentColorKey.b);

        if (deltaR < _TransparencyMargin && deltaG < _TransparencyMargin && deltaB < _TransparencyMargin)
        {
          return float4(0.0f, 0.0f, 0.0f, 0.0f);
        }

        return color;
      }
      ENDCG
    }
  }
}

新建Material(材质球),选择shader: c#脚本挂在摄像机上,摄像机的Clear Flags选择Solid Color模式,脚本上挂载刚刚新建的材质球