运行 Unity 在 Headless 模式下独立捕获屏幕截图

Run Unity standalone in Headless Mode whilst capturing screenshots

我需要创建一个以 Headless 模式运行的统一项目(使用 -batchmode 命令),但它必须捕获屏幕截图,例如每隔一秒并将它们写到一个文件中。

我知道在无头模式下,您需要强制调用 Camera.Render() 才能呈现任何内容。

截下第一张截图后,时间仿佛凝固了。第一个截图看起来完全正确,但所有后续截图都与第一个完全相同,暗示时间已经冻结。

我需要做什么才能确保场景随时间正确更新并且相机能够每秒渲染?

using System;
using System.Collections;
using System.IO;
using UnityEngine;

namespace Assets
{
    public class Particles : MonoBehaviour
    {
        private const float screenshotEvery = 1.0f;
        private float timerCount = 0f;
        private float elapsedSecond;
        private const float maxElapsedSecond = 20;
        private string screenshotsDirectory = "UnityHeadlessRenderingScreenshots";
        public Camera camOV;
        public RenderTexture currentRT;

        // Use this for initialization
        public void Start ()
        {
            Application.targetFrameRate = 60;
            if (Directory.Exists(screenshotsDirectory))
            {
                Directory.Delete(screenshotsDirectory, true);
            }
            if (!Application.isEditor)
            {
                Directory.CreateDirectory(screenshotsDirectory);
                camOV.targetTexture = currentRT;
            }

        }

        // Update is called once per frame
        public void Update ()
        {
            elapsedSecond += Time.deltaTime;
            timerCount += Time.deltaTime;
            if (Application.isEditor)
            {
                return;
            }
            if (elapsedSecond <= maxElapsedSecond)
            {
                if (timerCount >= screenshotEvery)
                {
                    TakeScreenShot();
                    timerCount = 0f;
                }
            }
            else
            {
                Application.Quit();
            }
        }

        public void TakeScreenShot()
        {
            RenderTexture.active = camOV.targetTexture;
            camOV.Render();
            Texture2D imageOverview = new Texture2D(camOV.targetTexture.width, camOV.targetTexture.height, TextureFormat.RGB24, false);
            imageOverview.ReadPixels(new Rect(0, 0, camOV.targetTexture.width, camOV.targetTexture.height), 0, 0);
            imageOverview.Apply();
            RenderTexture.active = currentRT;

            // Encode texture into PNG
            byte[] bytes = imageOverview.EncodeToPNG();

            // save in memory
            string filename = elapsedSecond + ".png";
            var path = screenshotsDirectory + "/" + filename;
            File.WriteAllBytes(path, bytes);

        }
    }
}

非常感谢!

关于截屏,我想给你的一个建议是等待 WaitForEndOfFrame 后再截屏。

你说你想每秒截图并保存。这应该在协程中而不是在更新函数中完成。

协程函数中的类似内容:

WaitForEndOfFrame waitForFrame = new WaitForEndOfFrame();
WaitForSeconds waitForTime = new WaitForSeconds(1);//1 second

while (true)
{
    //Wait for frame
    yield return waitForFrame;

    Increment timer

    Capture and save Screenshot

    Save the screenshot

    //Wait for one second 
    yield return waitForTime;
}

我这样做没有遇到任何问题。只需启动协程并让它在 while 循环中永远 运行 。它不会冻结,因为它每 1 秒和每一帧都产生一次。下面是完整的代码:

public Camera camOV;
public RenderTexture currentRT;
float elapsedSecond = 0;
string screenshotsDirectory = "UnityHeadlessRenderingScreenshots";
float beginTime;

void Start()
{
    beginTime = Time.time;
    StartCoroutine(TakeScreenShot());
}

public IEnumerator TakeScreenShot()
{
    beginTime = Time.time;

    WaitForEndOfFrame waitForFrame = new WaitForEndOfFrame();
    WaitForSeconds waitForTime = new WaitForSeconds(1);//1 second

    while (true)
    {
        //Wait for frame
        yield return waitForFrame;

        //Increment timer
        elapsedSecond = Time.time - beginTime;

        RenderTexture.active = camOV.targetTexture;
        camOV.Render();
        Debug.Log(camOV);
        Texture2D imageOverview = new Texture2D(camOV.targetTexture.width, camOV.targetTexture.height,
            TextureFormat.RGB24, false);
        imageOverview.ReadPixels(new Rect(0, 0, camOV.targetTexture.width, camOV.targetTexture.height), 0, 0);
        imageOverview.Apply();
        RenderTexture.active = currentRT;

        // Encode texture into PNG
        byte[] bytes = imageOverview.EncodeToPNG();

        // save in memory
        string filename = elapsedSecond + ".png";
        var path = screenshotsDirectory + "/" + filename;
        File.WriteAllBytes(path, bytes);

        //Wait for one second 
        yield return waitForTime;

        Debug.Log(elapsedSecond);
    }
}