【问题标题】:Take screenshot in Unity3D with no lags在 Unity3D 中截屏没有延迟
【发布时间】:2016-03-23 18:39:13
【问题描述】:

我已经尝试了每个变体:http://wiki.unity3d.com/index.php/ScreenCapture 毕竟,简单的 Application.Capturescreenshot(...) 是最不滞后的。

请告诉我,我怎样才能在没有任何延迟的情况下截取屏幕截图? Android上很多游戏都有这个功能,所以不是不可能的。

【问题讨论】:

  • 您可以将屏幕截图保存在另一个线程中,而不是将其保存在在主线程上执行的协程中,从而在您的应用程序中引入延迟。
  • @RCYR Capturescreenshot 只能从主线程调用。
  • 在制作屏幕截图视频时,用于更改帧速率的命令到底是什么?最近这里有一个问题..
  • 查看这个仓库:github.com/keijiro/AsyncCaptureTest。它使用异步 GPU 回读 API(在 Unity 2018.1 中引入)在不阻塞主线程的情况下捕获渲染。它的开发者在 Unity Japan 工作。

标签: c# android unity3d screen screenshot


【解决方案1】:

拍照时滞后原因是当您尝试同时执行所有步骤没有 等待 strong> 在每一步之后。要解决此问题,您执行一个操作并等待许多帧,然后执行下一个操作,然后再次等待。如果您决定在游戏过程中保存图片,请使用线程来保存图片。 EncodeToPNG() 是另一个问题,但我稍后会讨论它的另一个解决方案。

如果您想截屏然后在游戏运行时立即显示它,下面的代码很好。 它将以正确的方式拍照然后保存文件大约需要 1.5 秒。

 IEnumerator screenShotCoroutine()
{
    yield return new WaitForEndOfFrame();
    string path = Application.persistentDataPath + "/DrukaloScreenshot.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);

    //Wait for a long time
    for (int i = 0; i < 15; i++)
    {
        yield return null;
    }

    screenImage.Apply();

    //Wait for a long time
    for (int i = 0; i < 15; i++)
    {
        yield return null;
    }

    //Convert to png(Expensive)
    byte[] imageBytes = screenImage.EncodeToPNG();

    //Wait for a long time
    for (int i = 0; i < 15; i++)
    {
        yield return null;
    }

    //Create new thread then save image to file
    new System.Threading.Thread(() =>
    {
        System.Threading.Thread.Sleep(100);
        File.WriteAllBytes(path, imageBytes);
    }).Start();
}

如果您要拍照并且不需要立即显示图像,您可以将图片保存为raw Texture2D,然后当您想要稍后访问/加载/显示图片,您可以从 file读取,然后稍后将其转换为 PNG。这样做的原因是EncodeToPNG() 非常昂贵,所以如果您不需要立即使用图片,则无需将其转换为 png。 只有在您真正需要 png 时才能这样做。所以现在我们可以将它保存为“.t2D”然后加载它并将加载的图像转换为“.png”。

另存为 Texture2D ".t2D"

IEnumerator screenShotCoroutine()
    {
        yield return new WaitForEndOfFrame();
        string path = Application.persistentDataPath + "/DrukaloScreenshot.t2D";

        Texture2D screenImage = new Texture2D(Screen.width, Screen.height);

        //Get Picture
        screenImage.ReadPixels(new Rect(0, 0, Screen.width, Screen.height), 0, 0);

        //Wait for a long time
        for (int i = 0; i < 15; i++)
        {
            yield return null;
        }

        screenImage.Apply();

        //Wait for a long time
        for (int i = 0; i < 15; i++)
        {
            yield return null;
        }

        byte[] rawData = screenImage.GetRawTextureData();

        //Wait for a long time
        for (int i = 0; i < 15; i++)
        {
            yield return null;
        }

        //Create new thread then save image
        new System.Threading.Thread(() =>
        {
            System.Threading.Thread.Sleep(100);
            File.WriteAllBytes(path, rawData);
        }).Start();
    }

然后当你真正需要图像时,加载Texture2D“.t2D”,然后将其转换为“.png”,然后删除旧的“”。 t2D”。

IEnumerator screenShotCoroutine()
{
    string path = Application.persistentDataPath + "/DrukaloScreenshot.t2D";
    string newPath = Application.persistentDataPath + "/DrukaloScreenshot.png";

    Texture2D screenImage = new Texture2D(Screen.width, Screen.height);

    byte[] rawData = null;

    //Create new thread then Load image
    new System.Threading.Thread(() =>
    {
        System.Threading.Thread.Sleep(100);
        rawData = File.ReadAllBytes(path);
    }).Start();

    //Wait for a long time
    for (int i = 0; i < 9; i++)
    {
        yield return null;
    }

    //Put loaded bytes to Texture2D 
    screenImage.LoadRawTextureData(rawData);

    //Wait for a long time
    for (int i = 0; i < 9; i++)
    {
        yield return null;
    }

    screenImage.Apply();

    //Wait for a long time
    for (int i = 0; i < 9; i++)
    {
        yield return null;
    }

    //convert to png
    byte[] pngByte = screenImage.EncodeToPNG();

    //Wait for a long time
    for (int i = 0; i < 9; i++)
    {
        yield return null;
    }

    //Do whatever you want with the png file(display to screen or save as png)


    //Create new thread and Save the new png file, then Delete Old image(DrukaloScreenshot.t2D)
    new System.Threading.Thread(() =>
    {
        System.Threading.Thread.Sleep(100);
        File.WriteAllBytes(newPath, pngByte); //Save the new png file(DrukaloScreenshot.png)
        File.Delete(path); //Delete old file (DrukaloScreenshot.t2D)
    }).Start();
}

【讨论】:

  • 我使用了你的代码,在游戏过程中制作屏幕图像时出现了同样的延迟。与 Application.Capturescreenshot(...) 一样,延迟有点不明显。无论如何,谢谢。它更好,但只是一点点
  • 你的意思是我的解决方案比 Application.Capturescreenshot(...) 好 20% 或 Application.Capturescreenshot(...) 比我的解决方案好?
  • 这里是怎么回事:首先我在小米红米 Note 2 上测试,你的解决方案比 Application.Capturescreenshot(...) 好 20%,我给你写了答案,比我在另一部手机上测试,这比 Note 2 弱一点,而且你的建议比 Application.Capturescreenshot(...) 差得多,它能让游戏保持 0.7 秒左右。
  • 好吧,我不知道该告诉你什么。我在 Windows 上对其进行了测试,并使用分析器尽可能地减少了减速。也可以在 iphone 上测试。这应该会产生很大的不同,因为那里有很多 android 设备,而一个不好的可能是因为它是低端设备。
  • 首先是处理器Mediatec X10而不是Snapdragon 615。615不是低端处理器,它是中档处理器。不过,感谢您的代码建议。
【解决方案2】:

Application.CaptureScreenshot() 通常很慢,更快的方法是使用Texture2D 并使用ReadPixels() 函数读取屏幕上的所有像素,而不是编码并将纹理保存到硬盘驱动器。

这是这个方法的一个例子:

Texture2D screenShot = new Texture2D(Screen.width, Screen.height);
screenShot.ReadPixels(new Rect(0,0,Screen.width,Screen.height),0,0);
screenShot.Apply();
byte[] bytes = tex.EncodeToPNG();
Object.Destroy(tex);
File.WriteAllBytes(Application.dataPath + "/../myScreenShot.png", bytes);

如需完整参考,您可以使用 Unity 脚本参考页面 here

【讨论】:

  • ReadPixels() 非常慢,因为它会触发 GPU 刷新,这意味着等待当前命令缓冲区中的所有剩余命令执行
  • @Aray 那么有什么替代方案?
  • @pookie 不幸的是我没有。 udacity和nvidia有一个项目。他们可能以某种方式做到了github.com/udacity/self-driving-car-sim
  • @Aray 谢谢,我查过了。他们使用与我完全相同的技术。请参阅他们的代码中的 1_SelfDrivingCar/scripts/camera helper.cs。
猜你喜欢
  • 1970-01-01
  • 2016-10-17
  • 2020-12-12
  • 2016-04-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-12-05
  • 1970-01-01
相关资源
最近更新 更多