3 Ways To Capture A Screenshot In Unity3D

For my equally ridiculous follow-up to Brick Buddies, I need to save a screenshot both as a texture and as a file in Unity3D. Although the game I’m currently writing has screenshots as an integral gameplay element, it’s still useful to integrate screenshots into any Unity3D project. Using Prime31’s Social Networking plug-in, it’s possible to Tweet pictures or post screens to user’s Facebook galleries. Having a screenshot capture feature can boost your viral reach. Especially if you design your application in such a way that people want to share screenshots. In Unity3D, there are a number of ways to do this.

Application.CaptureScreenshot

CaptureScreenshot is a method in the Application class that does exactly what it says; it saves the screenshot as a PNG file. On iOS devices, this screenshot will be put in the Documents folder. On other platforms you can specify an absolute path to put the file anywhere.

What the documentation doesn’t tell you is CaptureScreenshot is asynchronous. This is because capturing and saving the screen can take awhile. The API call itself isn’t a coroutine, so there’s no easy way to monitor its progress. One hack is to write your own method which checks for the existence of the screenshot file. Once the screenshot has completed saving, the file will be there.

Also note that it’s good practice to put CaptureScreenshot calls in the LateUpdate method. This way you capture the contents of the frame as it will look at the end of that update. If you have made any objects active or inactive during that frame, the results of those operations will be seen in LateUpdate.

RenderTexture

One convoluted way to take a screenshot is to use the RenderTexture feature in Unity Pro. You can create a RenderTexture object and tell any camera to write to it. You can then access the color buffer of the RenderTexture if you want to write out the pixels to a PNG.

CaptureScreenshot was just way too slow for my needs (I needed immediate access to the frame buffer) so I started writing a RenderTexture solution, until I found an easier way. If you want to check out this technique, I suggest this example.

Texture2D.ReadPixels

ReadPixels will read the pixel data from a specified rectangle on the screen and copy it to the source texture. It’s fairly fast and works with a few lines of code. Much like CaptureScreenshot, it’s a good idea to queue up the action somehow and then actually call ReadPixels in LateUpdate. Works like a charm:

Texture2D tex = new Texture2D(Screen.width, Screen.height);
tex.ReadPixels(new Rect(0,0,Screen.width,Screen.height),0,0);
tex.Apply();

ReadPixels still introduces some delay, so in the end I might try to see if RenderTexture is faster. If you are using the screenshots as a real-time texture effect, then RenderTexture is your best bet.

27 thoughts on “3 Ways To Capture A Screenshot In Unity3D

  1. Hi Ralph, this is very useful. I’ve been struggling with creating a png from a camera that isn’t the Main view camera, and from your tips i’m headed in the right direction. I was wondering if you could clarify (or send me a link to somewhere that clarifies — i can’t find anything) what you mean by “access the color buffer of the RenderTexture.” I’ve got my rendertexture set up, but I don’t understand how to use the color buffer to encode the png, and I can’t find any examples.

  2. Hey,

    Thanks for the info.
    I’ve used CaptureScreenshot, then WWW to apply it to a texture. Works perfectly in Editor, but when i build to iOS, no luck. My code is:

    IEnumerator TakeScreenshotAndLoadItIntoTexture(string filename) {

    Application.CaptureScreenshot(filename);

    /*while(!File.Exists(filename)){
    debugString = “Saving”;
    yield return null;
    }*/

    FileInfo info = new FileInfo(filename);
    if(info == null || info.Exists == false){
    yield return null;
    }

    debugString = “Saved”;
    WWW screenShot = new WWW(“file://” + filename);
    //while(!screenShot.isDone)
    yield return screenShot;
    print (“Loading”);
    debugString = “Loading”;

    print(“Done ” + Time.time);

    previewTexture = screenShot.texture;
    print (previewTexture.name);
    print (“Width” + previewTexture.width);
    debugString = “Width: ” + previewTexture.width + “, Name: ” + previewTexture.name;
    preview = true;

    }

    any ides?

    Thanks man.

    • I ended up not using CaptureScreenshot because it’s way too slow. ReadPixels or a RenderTexture is really the way to go. Is it possible the path is wrong on iOS? You might have to use a specific path to where it deposits the screenshot file? It’s been awhile since I tried using CaptureScreenshot for this–I’m not sure exactly where it puts the screenshot file.

      • Hey Man,

        Thanks for the reply. I’ll try ReadPixels again, but had some strange results when i did before, specifically the displayed texture being offset. Is it possible to then save this texture as a png, because in the end i want to be posting these to facebook.

        Cheers.

      • Yeah–ReadPixels doesn’t work for me on The New iPad. I suppose it chokes on the retina resolution. So on the New iPad I had to use RenderTexture…which doesn’t really work because it has to be a power of 2. So I just used 1024×2048 to approximate the retina aspect ratio.

        I guess there is no perfect solution. But I still think you might have a path issue on iOS.

        Also remember they changed ReadPixels in 3.5 so that it only works on OnPostRender.

  3. You’re right, it is a path issue, this is why my check for file yields are never terminating, i printed the persistentDataPath, which i’m writing to and reading from, so i know it exists, which is nice. How can it write to a location and then not be able to read it with exactly the same input?? The files are there, baffling.

    • As I recall CaptureScreenshot puts the file in whatever folder it wants on iOS. I think you don’t specify the full path and it will put it in a default directory. And then you use whatever that path is to grab the file later. I think that it doesn’t work on iOS if you specify more than a filename. Using a full path breaks it.

  4. Hi there!
    Thanks for this tutorial!

    I was wondering something though, I would like to share this screen shot afterward by texting or mail like many many applications can do.

    But I can’t really find how to do it. Do I have to write a plugin for that? Have you heard of a simple way to do that?

    • Well I believe Prime31’s Miscellaneous plug-in has support for the email screen on iOS and Android. I used his social networking plug-ins to share pictures via Twitter and FB. It was pretty straightforward.

  5. I need solution for flash build as ReadPixels method is not supported by flash.
    RenderTexture is what I am able to implement but after making the flash build texture color turns to black.

  6. Hello! I’m using this code bat don’t work.
    Sorry for my ingles is too bad.
    Can you help me? Thanks.

    function OnGUI(){
    // Boton Sacar Foto
    if(GUI.Button(Rect(Screen.width – 95, 80, 90, 35), “Foto”))
    {
    Application.CaptureScreenshot(“/storage/sdcard0/Download/Screenshot.png”);
    }
    }

  7. Hi
    I would want to put my screen shots in the folder DATA / MY_DOCUMENT / SCREEN_SHOOT.
    What have to I write?
    —————–
    if ( Input.GetKeyUp( KeyCode.F3 ) )
    {
    Debug.Log(“Screen captured.”);
    Application.CaptureScreenshot(“Screenshot.png”); ????
    }
    ————————

  8. Pingback: FastTrick - Screenshots in Unity - Aliasing Games

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s