C# 使用ScreenCapture捕获并保存屏幕截图。捕获屏幕截图

C# 使用ScreenCapture捕获并保存屏幕截图。捕获屏幕截图,c#,ios,unity3d,screenshot,C#,Ios,Unity3d,Screenshot,我一直在尝试拍摄一张截图,然后立即使用它来显示某种预览,有时有效,有时无效,我目前不在工作,我在这台计算机中没有unity,因此我将尝试动态地重新创建它(可能会有一些语法错误) 公共游戏对象截屏预览; 公共屏幕截图(){ 字符串imageName=“screenshot.png”; //截图 ScreenCapture.CaptureScreenshot(imageName); //从文件中读取数据 byte[]data=File.ReadAllBytes(Application.persist

我一直在尝试拍摄一张截图,然后立即使用它来显示某种预览,有时有效,有时无效,我目前不在工作,我在这台计算机中没有unity,因此我将尝试动态地重新创建它(可能会有一些语法错误)

公共游戏对象截屏预览;
公共屏幕截图(){
字符串imageName=“screenshot.png”;
//截图
ScreenCapture.CaptureScreenshot(imageName);
//从文件中读取数据
byte[]data=File.ReadAllBytes(Application.persistentDataPath+“/”+imageName);
//创建纹理
Texture2D screenshotTexture=新的Texture2D(屏幕.宽度,屏幕.高度);
//加载图像
screenshotTexture.LoadImage(数据);
//创造一个精灵
Sprite screenshotSprite=Sprite.Create(屏幕快照纹理,新矩形(0,0,Screen.width,Screen.height),新矢量2(0.5f,0.5f));
//将精灵设置为屏幕快照预览
screenshotPreview.GetComponent().sprite=screenshotSprite;
}
据我所知,ScreenCapture.CaptureScreenshot不是异步的,因此在我尝试加载数据之前应该已经写入图像,但问题是,正如我之前所说,它不起作用,并且加载了带有红色问号的8x8纹理,这显然是纹理无法加载,但文件应该在那里,所以我无法理解为什么没有正确加载

我尝试过的另一件事(这很恶心,但我已经厌倦了这一点,也没有办法了)是使用更新方法等待一段时间,然后执行代码加载数据并创建纹理、精灵并显示它,但即使如此,它也会失败几次,比以前更频繁,但仍然失败,这让我相信,即使文件是创建的,它也没有完成beign编写,有人知道解决方法吗?任何建议都将不胜感激


作为额外信息,此项目正在iOS设备中运行。

我在文档中没有看到任何说明它不是异步的。事实上,对于Android(如果我读对了的话),它明确表示它是异步的

也就是说,我会尝试在找不到文件时暂停。把它放在一个联合程序中

FileInfo yourFile = new FileInfo("YourFile.png");
while (File.Exists(yourFile.name) || IsFileLocked(yourFile)) 
    yield return null;

您还可以尝试在那里进行一些调试检查,以查看文件出现(假设它曾经出现)之前需要多长时间(秒或帧)


编辑:正如derHugo指出的,文件的存在并不意味着文件已经准备好了。我已经编辑了代码来处理这个问题!但是它仍然不包括文件已经存在的情况,在这种情况下,您可能需要一个动态文件名,比如带有时间戳,或者您希望先删除文件

众所周知,
screenscapture.CaptureScreenshot
函数存在许多问题。是另一个

以下是其网站的一段引述:

在Android上,此函数立即返回。结果截图 稍后提供

iOS行为没有文档记录,但我们可以假设iOS上的行为是相同的。截图后等待几帧,然后再尝试读取/加载

public IEnumerator TakeScreenshot()
{

    string imageName = "screenshot.png";

    // Take the screenshot
    ScreenCapture.CaptureScreenshot(imageName);

    //Wait for 4 frames
    for (int i = 0; i < 5; i++)
    {
        yield return null;
    }

    // Read the data from the file
    byte[] data = File.ReadAllBytes(Application.persistentDataPath + "/" + imageName);

    // Create the texture
    Texture2D screenshotTexture = new Texture2D(Screen.width, Screen.height);

    // Load the image
    screenshotTexture.LoadImage(data);

    // Create a sprite
    Sprite screenshotSprite = Sprite.Create(screenshotTexture, new Rect(0, 0, Screen.width, Screen.height), new Vector2(0.5f, 0.5f));

    // Set the sprite to the screenshotPreview
    screenshotPreview.GetComponent<Image>().sprite = screenshotSprite;

}

程序员的代码通过如下调用成功运行。它被设计为协同路由,因此不会干扰帧速率。因此,它应该被称为协同程序。确保CallerObject是从“MonoBehavior”继承的


我只是在读关于这种方式的书,但事实上,你提出这样做让我感觉更好,我明天会尝试一下,谢谢!我认为没有
截屏的第二个示例不是异步的,它会阻塞UI线程,尤其是
EncodeToPNG
(您可能需要
EncodeToJPG
以获得更小的文件大小-系数可能超过10)。。。尽管如此,它似乎更可靠^^^您还可以通过在第一种方法中写入结果来进一步改进UI.block,5帧的意义是什么?@Mars it's 4帧(5是独占的)。但是我同意:因为在文档中它说,
捕获截图会在Android上立即返回。屏幕截图在后台继续。生成的屏幕截图在几秒钟后保存在文件系统中。
我们应该使用
yield return new WaitForSeconds(XY)
来实际等待几秒钟,而不是frames@derHugo0-1-2-3-4等于5帧,但我认为等待xy秒也不能保证。除非CaptureScreenshot有一个已知的超时,否则我可能是错的,但是当流仍在写入时,
file.found
不能找到该文件吗?当然,如果您想覆盖该文件而不是创建一个新文件,这也可能不起作用everytime@derHugo很抱歉,忘记标记youCoroutines仍在主线程上运行,这将干扰您的帧速率。关键是该过程不会等待图像捕获完成。如果它按设计工作,即使它在主线程上运行,它也会共享进程时间。这是什么让它不等待?如果我正确理解了这段代码,它会一直等到帧结束,而不是在更新过程中,但是如果读/写需要2秒,那么这一帧的长度仍将超过2秒。该过程不会等待图像捕获完成。但图像捕获将等待帧结束。因此,无论在哪个阶段调用此函数,都将完成,但帧仍需要2秒。这不会保存您的帧速率
public IEnumerator TakeScreenshot()
{

    string imageName = "screenshot.png";

    // Take the screenshot
    ScreenCapture.CaptureScreenshot(imageName);

    //Wait for 4 frames
    for (int i = 0; i < 5; i++)
    {
        yield return null;
    }

    // Read the data from the file
    byte[] data = File.ReadAllBytes(Application.persistentDataPath + "/" + imageName);

    // Create the texture
    Texture2D screenshotTexture = new Texture2D(Screen.width, Screen.height);

    // Load the image
    screenshotTexture.LoadImage(data);

    // Create a sprite
    Sprite screenshotSprite = Sprite.Create(screenshotTexture, new Rect(0, 0, Screen.width, Screen.height), new Vector2(0.5f, 0.5f));

    // Set the sprite to the screenshotPreview
    screenshotPreview.GetComponent<Image>().sprite = screenshotSprite;

}
IEnumerator captureScreenshot()
{
    yield return new WaitForEndOfFrame();

    string path = Application.persistentDataPath + "Screenshots/"
            + "_" + screenshotCount + "_" + Screen.width + "X" + Screen.height + "" + ".png";

    Texture2D screenImage = new Texture2D(Screen.width, Screen.height);
    //Get Image from screen
    screenImage.ReadPixels(new Rect(0, 0, Screen.width, Screen.height), 0, 0);
    screenImage.Apply();
    //Convert to png
    byte[] imageBytes = screenImage.EncodeToPNG();

    //Save image to file
    System.IO.File.WriteAllBytes(path, imageBytes);
}
 public class CallerObject : MonoBehaviour
{
    public void Caller()
    {
        String imagePath = Application.persistentDataPath + "/image.png";
        StartCoroutine(captureScreenshot(imagePath));
    }


    IEnumerator captureScreenshot(String imagePath)
    {
        yield return new WaitForEndOfFrame();
        //about to save an image capture
        Texture2D screenImage = new Texture2D(Screen.width, Screen.height);


        //Get Image from screen
        screenImage.ReadPixels(new Rect(0, 0, Screen.width, Screen.height), 0, 0);
        screenImage.Apply();

        Debug.Log(" screenImage.width" + screenImage.width + " texelSize" + screenImage.texelSize);
        //Convert to png
        byte[] imageBytes = screenImage.EncodeToPNG();

        Debug.Log("imagesBytes=" + imageBytes.Length);

        //Save image to file
        System.IO.File.WriteAllBytes(imagePath, imageBytes);
    }
}