如何创建透明 Unity 的屏幕截图 window + OS
How to create a screenshot of a transparant Unity window + OS
因此,对于我们正在尝试创建的应用程序,我们希望将独立应用程序的 Unity window 透明化(除了几个按钮之外的所有内容)并让用户对其 view/OS+统一层在一起。
所以例子:
用户打开我们的应用程序,点击一个按钮,整个单元 window 除了几个按钮变成透明的。然后用户可以像平常一样使用它的 OS,而前面提到的按钮保持在顶部。然后用户可以单击按钮创建他们的 OS 的屏幕截图,然后我们会将其保存到他们的系统中。例如,通过屏幕截图,我们可以在用户 OS 上方显示 Unity 中的任何内容(3D 模型、图像)。
目前,我们可以使用如下类似设置将 window 变为透明:https://alastaira.wordpress.com/2015/06/15/creating-windowless-unity-applications/
效果很好,点击 windows 等也很好。但是,我们现在想创建一个屏幕截图并将其保存在某个地方。为此,我们尝试了多种方法,几年前,在我们将这个项目搁置一旁之前,我们通过使用我们从 unity 内部调用的 "using System.Drawing" 代码的自定义 dll 让它工作。请参阅下面此 dll 和代码的示例。
using System.Drawing;
namespace ScreenShotDll
{
public class ScreenShotClass
{
public static void TakeScreenShotRect(int srcX, int srcY, int dstX, int dstY) //both fullscreen screenshot and cropped rectangle screenshot
{
int width = Math.Abs(srcX - dstX);
int height = Math.Abs(srcY - dstY);
Bitmap memoryImage;
memoryImage = new Bitmap(width, height);
Size s = new Size(memoryImage.Width, memoryImage.Height);
Graphics memoryGraphics = Graphics.FromImage(memoryImage);
memoryGraphics.CopyFromScreen(srcX, srcY, 0, 0, s);
string str = "";
try
{
str = string.Format(AppDomain.CurrentDomain.BaseDirectory + @"Screenshot.png");
}
catch (Exception er)
{
Console.WriteLine("Sorry, there was an error: " + er.Message);
Console.WriteLine();
}
memoryImage.Save(str);
}
然而,这似乎不再奏效了。我们在 Unity 的 IL2CPP 后端上得到错误:NotSupportedException: System.Drawing.Bitmap
我们也在尝试使用 Unity 中的 user32.dll 并使用它的 GetPixel、ReleaseDC 和 GetActiveWindow 函数,正如在几个论坛上发布的那样,但我们得到的只是一张白色图像.
我们将不胜感激调整我们的自定义 dll 的任何方法或任何其他方法。如果您需要更多信息,请告诉我。
几天后,我设法在 Unity 中解决了这个问题。
我使用了以下代码,制作了 window 的屏幕截图,并将其保存为位图。然后将其保存在我的磁盘上为.png。
[DllImport("user32.dll", SetLastError = true)] static extern int GetSystemMetrics(int smIndex);
[DllImport("user32.dll", SetLastError = false)] static extern IntPtr GetDesktopWindow();
[DllImport("gdi32.dll", EntryPoint = "CreateCompatibleDC", SetLastError=true)] static extern IntPtr CreateCompatibleDC([In] IntPtr hdc);
[DllImport("gdi32.dll", EntryPoint = "DeleteDC")] public static extern bool DeleteDC([In] IntPtr hdc);
[DllImport("gdi32.dll", EntryPoint = "DeleteObject")] public static extern bool DeleteObject([In] IntPtr hObject);
[DllImport("gdi32.dll", EntryPoint = "CreateCompatibleBitmap")] static extern IntPtr CreateCompatibleBitmap([In] IntPtr hdc, int nWidth, int nHeight);
[DllImport("gdi32.dll", EntryPoint = "SelectObject")] public static extern IntPtr SelectObject([In] IntPtr hdc, [In] IntPtr hgdiobj);
[DllImport("gdi32.dll", EntryPoint = "BitBlt", SetLastError = true)] static extern bool BitBlt([In] IntPtr hdc, int nXDest, int nYDest, int nWidth, int nHeight, [In] IntPtr hdcSrc, int nXSrc, int nYSrc, uint dwRop);
private void screenShot()
{
Debug.Log("In screenShot!");
int nScreenWidth = GetSystemMetrics(0);
int nScreenHeight = GetSystemMetrics(1);
IntPtr hDesktopWnd = GetDesktopWindow();
IntPtr hDesktopDC = GetDC(hDesktopWnd);
IntPtr hCaptureDC = CreateCompatibleDC(hDesktopDC);
IntPtr hCaptureBitmap = CreateCompatibleBitmap(hDesktopDC, nScreenWidth, nScreenHeight);
SelectObject(hCaptureDC, hCaptureBitmap);
BitBlt(hCaptureDC, 0, 0, nScreenWidth, nScreenHeight, hDesktopDC, 0, 0, 0x00CC0020 | 0x40000000);
Bitmap bmp = Image.FromHbitmap(hCaptureBitmap);
ImageConverter converter = new ImageConverter();
byte[] bytes = (byte[])converter.ConvertTo(bmp, typeof(byte[]));
string path = Application.dataPath;
if (Application.platform == RuntimePlatform.OSXPlayer) {
path += "/../../";
}
else if (Application.platform == RuntimePlatform.WindowsPlayer) {
path += "/../";
}
File.WriteAllBytes(path + "Screenshot" + ".png", bytes);
ReleaseDC(hDesktopWnd, hDesktopDC);
DeleteDC(hCaptureDC);
DeleteObject(hCaptureBitmap);
}
我能够通过创建 class 库来获取屏幕截图。我将我的项目设置为 .Net 4.7.2 库,因此这可能对您不起作用 (IL2CPP)。我将我的统一项目脚本版本设置为 .Net 4.x 我不确定是否有必要。我只是将我的库 dll 文件复制到资产文件夹中。
这些是我需要的 using 语句,CapturePng 从 Unity 获取内存流。
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
public static void CapturePng(MemoryStream memoryStream, int width, int height, int resolutionX, int resolutionY)
{
//Create a new bitmap.
using (var bmpScreenshot = new Bitmap(
width,
height,
PixelFormat.Format32bppArgb))
{
// Create a graphics object from the bitmap.
using (var gfxScreenshot = Graphics.FromImage(bmpScreenshot))
{
// Take the screenshot from the upper left corner to the right bottom corner.
gfxScreenshot.CopyFromScreen(
new Point(0, 0),
new Point(0, 0),
new Size(width, height),
CopyPixelOperation.SourceCopy);
// Save the screenshot to the specified path that the user has chosen.
new Bitmap(bmpScreenshot, new Size(resolutionX, resolutionY)).Save(memoryStream, ImageFormat.Png);
}
}
}
这就是我的 Unity 代码的样子。
Texture2D tex = new Texture2D(200, 300, TextureFormat.RGB24, false);
MemoryStream ms = new MemoryStream();
Screenshot.CapturePng(ms, Screen.currentResolution.width, Screen.currentResolution.height);
ms.Seek(0, SeekOrigin.Begin);
tex.LoadImage(ms.ToArray());
MeshRenderer.material.mainTexture = tex;
我希望这对您有所帮助,至少它不需要任何“user32.dll”导入或“gdi32.dll”导入。
因此,对于我们正在尝试创建的应用程序,我们希望将独立应用程序的 Unity window 透明化(除了几个按钮之外的所有内容)并让用户对其 view/OS+统一层在一起。
所以例子: 用户打开我们的应用程序,点击一个按钮,整个单元 window 除了几个按钮变成透明的。然后用户可以像平常一样使用它的 OS,而前面提到的按钮保持在顶部。然后用户可以单击按钮创建他们的 OS 的屏幕截图,然后我们会将其保存到他们的系统中。例如,通过屏幕截图,我们可以在用户 OS 上方显示 Unity 中的任何内容(3D 模型、图像)。
目前,我们可以使用如下类似设置将 window 变为透明:https://alastaira.wordpress.com/2015/06/15/creating-windowless-unity-applications/
效果很好,点击 windows 等也很好。但是,我们现在想创建一个屏幕截图并将其保存在某个地方。为此,我们尝试了多种方法,几年前,在我们将这个项目搁置一旁之前,我们通过使用我们从 unity 内部调用的 "using System.Drawing" 代码的自定义 dll 让它工作。请参阅下面此 dll 和代码的示例。
using System.Drawing;
namespace ScreenShotDll
{
public class ScreenShotClass
{
public static void TakeScreenShotRect(int srcX, int srcY, int dstX, int dstY) //both fullscreen screenshot and cropped rectangle screenshot
{
int width = Math.Abs(srcX - dstX);
int height = Math.Abs(srcY - dstY);
Bitmap memoryImage;
memoryImage = new Bitmap(width, height);
Size s = new Size(memoryImage.Width, memoryImage.Height);
Graphics memoryGraphics = Graphics.FromImage(memoryImage);
memoryGraphics.CopyFromScreen(srcX, srcY, 0, 0, s);
string str = "";
try
{
str = string.Format(AppDomain.CurrentDomain.BaseDirectory + @"Screenshot.png");
}
catch (Exception er)
{
Console.WriteLine("Sorry, there was an error: " + er.Message);
Console.WriteLine();
}
memoryImage.Save(str);
}
然而,这似乎不再奏效了。我们在 Unity 的 IL2CPP 后端上得到错误:NotSupportedException: System.Drawing.Bitmap
我们也在尝试使用 Unity 中的 user32.dll 并使用它的 GetPixel、ReleaseDC 和 GetActiveWindow 函数,正如在几个论坛上发布的那样,但我们得到的只是一张白色图像.
我们将不胜感激调整我们的自定义 dll 的任何方法或任何其他方法。如果您需要更多信息,请告诉我。
几天后,我设法在 Unity 中解决了这个问题。
我使用了以下代码,制作了 window 的屏幕截图,并将其保存为位图。然后将其保存在我的磁盘上为.png。
[DllImport("user32.dll", SetLastError = true)] static extern int GetSystemMetrics(int smIndex);
[DllImport("user32.dll", SetLastError = false)] static extern IntPtr GetDesktopWindow();
[DllImport("gdi32.dll", EntryPoint = "CreateCompatibleDC", SetLastError=true)] static extern IntPtr CreateCompatibleDC([In] IntPtr hdc);
[DllImport("gdi32.dll", EntryPoint = "DeleteDC")] public static extern bool DeleteDC([In] IntPtr hdc);
[DllImport("gdi32.dll", EntryPoint = "DeleteObject")] public static extern bool DeleteObject([In] IntPtr hObject);
[DllImport("gdi32.dll", EntryPoint = "CreateCompatibleBitmap")] static extern IntPtr CreateCompatibleBitmap([In] IntPtr hdc, int nWidth, int nHeight);
[DllImport("gdi32.dll", EntryPoint = "SelectObject")] public static extern IntPtr SelectObject([In] IntPtr hdc, [In] IntPtr hgdiobj);
[DllImport("gdi32.dll", EntryPoint = "BitBlt", SetLastError = true)] static extern bool BitBlt([In] IntPtr hdc, int nXDest, int nYDest, int nWidth, int nHeight, [In] IntPtr hdcSrc, int nXSrc, int nYSrc, uint dwRop);
private void screenShot()
{
Debug.Log("In screenShot!");
int nScreenWidth = GetSystemMetrics(0);
int nScreenHeight = GetSystemMetrics(1);
IntPtr hDesktopWnd = GetDesktopWindow();
IntPtr hDesktopDC = GetDC(hDesktopWnd);
IntPtr hCaptureDC = CreateCompatibleDC(hDesktopDC);
IntPtr hCaptureBitmap = CreateCompatibleBitmap(hDesktopDC, nScreenWidth, nScreenHeight);
SelectObject(hCaptureDC, hCaptureBitmap);
BitBlt(hCaptureDC, 0, 0, nScreenWidth, nScreenHeight, hDesktopDC, 0, 0, 0x00CC0020 | 0x40000000);
Bitmap bmp = Image.FromHbitmap(hCaptureBitmap);
ImageConverter converter = new ImageConverter();
byte[] bytes = (byte[])converter.ConvertTo(bmp, typeof(byte[]));
string path = Application.dataPath;
if (Application.platform == RuntimePlatform.OSXPlayer) {
path += "/../../";
}
else if (Application.platform == RuntimePlatform.WindowsPlayer) {
path += "/../";
}
File.WriteAllBytes(path + "Screenshot" + ".png", bytes);
ReleaseDC(hDesktopWnd, hDesktopDC);
DeleteDC(hCaptureDC);
DeleteObject(hCaptureBitmap);
}
我能够通过创建 class 库来获取屏幕截图。我将我的项目设置为 .Net 4.7.2 库,因此这可能对您不起作用 (IL2CPP)。我将我的统一项目脚本版本设置为 .Net 4.x 我不确定是否有必要。我只是将我的库 dll 文件复制到资产文件夹中。
这些是我需要的 using 语句,CapturePng 从 Unity 获取内存流。
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
public static void CapturePng(MemoryStream memoryStream, int width, int height, int resolutionX, int resolutionY)
{
//Create a new bitmap.
using (var bmpScreenshot = new Bitmap(
width,
height,
PixelFormat.Format32bppArgb))
{
// Create a graphics object from the bitmap.
using (var gfxScreenshot = Graphics.FromImage(bmpScreenshot))
{
// Take the screenshot from the upper left corner to the right bottom corner.
gfxScreenshot.CopyFromScreen(
new Point(0, 0),
new Point(0, 0),
new Size(width, height),
CopyPixelOperation.SourceCopy);
// Save the screenshot to the specified path that the user has chosen.
new Bitmap(bmpScreenshot, new Size(resolutionX, resolutionY)).Save(memoryStream, ImageFormat.Png);
}
}
}
这就是我的 Unity 代码的样子。
Texture2D tex = new Texture2D(200, 300, TextureFormat.RGB24, false);
MemoryStream ms = new MemoryStream();
Screenshot.CapturePng(ms, Screen.currentResolution.width, Screen.currentResolution.height);
ms.Seek(0, SeekOrigin.Begin);
tex.LoadImage(ms.ToArray());
MeshRenderer.material.mainTexture = tex;
我希望这对您有所帮助,至少它不需要任何“user32.dll”导入或“gdi32.dll”导入。