Day 14 : How to take Screenshots within an App using Unity.


Introduction

In this article, we’ll look at creating a Screenshot capability for any AR Application. In fact, this will work on non-ar projects and apps as well. 

We all had the feature request or want to take screenshots within our app and share them directly instead of the users using the os shortcuts to snap the screenshots. This becomes particularly important in AR apps to share the experience directly with others.

Hence, today we’ll write a script that can be attached to any Gameobject within Unity that can take screenshots by the action of a button or you can call the methods within your own classes.

Getting Started

This will be a cross-platform compatible script and will work on Android, iOS and even on desktop projects.

Prerequisites

  1. Unity 2019.2.18f1.
  2. C# programming and understanding.

Implementation

The approach we are going to take will take High-resolution screenshots with arbitrary resolution. We will use the Unity MainCamera that renders everything to create a Texture2D. The Texture2D will then be converted into a .png and stored to the local disk storage.

We will support multiple file formats like jpeg, jpg, raw, and ppm. These can be configured from the editor itself. You could also use the ppm format to create a video by taking screenshots in series and combining them.

ScreenCapture Class

First the declared public variables.

    //Set your screenshot resolutions
    public int captureWidth = 1920;
    public int captureHeight = 1080;

    // configure with raw, jpg, png, or ppm (simple raw format)
    public enum Format { RAW, JPG, PNG, PPM };
    public Format format = Format.JPG;

    // folder to write output (defaults to data path)
    private string outputFolder;

    // private variables needed for screenshot
    private Rect rect;
    private RenderTexture renderTexture;
    private Texture2D screenShot;

You can set any resolution you’d the screenshot to be in with the captureHeight and Width variables.

We will use the set format from the editor to save it to that format while writing to the disk.

The output folder will be created under the PersistentDataPath directory of the app’s install location. This will vary between hardware.

Next, let’s initialize our output directory by creating a new folder if it doesn’t exist. We do in the Start() method. This will also print the output location to the debug console so you can see where the screenshots are being saved. You can also look at the API here to find the location folders for each platform.

    //Initialize Directory
    private void Start()
    {
        outputFolder = Application.persistentDataPath + "/Screenshots/";
        if(!Directory.Exists(outputFolder))
        {
            Directory.CreateDirectory(outputFolder);
            Debug.Log("Save Path will be : " + outputFolder);
        }
    }

Next, the helper method for creating a new unique filename.

    private string CreateFileName(int width, int height)
    {
        //timestamp to append to the screenshot filename
        string timestamp = DateTime.Now.ToString("yyyyMMddTHHmmss");

        // use width, height, and timestamp for unique file 
        var filename = string.Format("{0}/screen_{1}x{2}_{3}.{4}", outputFolder, width, height, timestamp, format.ToString().ToLower());

        // return filename
        return filename;
    }

Finally, the primary method. Make sure you’re project has a Camera setup with the MainCamera tag. Or else, you can also pass in a Camera object separately as well.

private void CaptureScreenshot()
{
    isProcessing = true;

    // create screenshot objects
    if (renderTexture == null)
    {
        // creates off-screen render texture to be rendered into
        rect = new Rect(0, 0, captureWidth, captureHeight);
        renderTexture = new RenderTexture(captureWidth, captureHeight, 24);
        screenShot = new Texture2D(captureWidth, captureHeight, TextureFormat.RGB24, false);
    }

    // get main camera and render its output into the off-screen render texture created above
    Camera camera = Camera.Main.
    camera.targetTexture = renderTexture;
    camera.Render();

    // mark the render texture as active and read the current pixel data into the Texture2D
    RenderTexture.active = renderTexture;
    screenShot.ReadPixels(rect, 0, 0);

    // reset the textures and remove the render texture from the Camera since were done reading the screen data
    camera.targetTexture = null;
    RenderTexture.active = null;

    // get our filename
    string filename = CreateFileName((int)rect.width, (int)rect.height);

    // get file header/data bytes for the specified image format
    byte[] fileHeader = null;
    byte[] fileData = null;

    //Set the format and encode based on it
    if (format == Format.RAW)
    {
        fileData = screenShot.GetRawTextureData();
    }
    else if (format == Format.PNG)
    {
        fileData = screenShot.EncodeToPNG();
    }
    else if (format == Format.JPG)
    {
        fileData = screenShot.EncodeToJPG();
    }
    else //For ppm files
    {
        // create a file header - ppm files
        string headerStr = string.Format("P6\n{0} {1}\n255\n", rect.width, rect.height);
        fileHeader = System.Text.Encoding.ASCII.GetBytes(headerStr);
        fileData = screenShot.GetRawTextureData();
    }

    // create new thread to offload the saving from the main thread
    new System.Threading.Thread(() =>
    {
        var file = System.IO.File.Create(filename);
        if (fileHeader != null)
        {
            file.Write(fileHeader, 0, fileHeader.Length);
        }
        file.Write(fileData, 0, fileData.Length);
        file.Close();
        Debug.Log(string.Format("Screenshot Saved {0}, size {1}", filename, fileData.Length));
        isProcessing = false;
    }).Start();

    //Cleanup
    Destroy(renderTexture);
    renderTexture = null;
    screenShot = null;
}

We then expose this method with a new TakeScreenshot() method.

    public void TakeScreenShot()
    {
        if (!isProcessing)
        {
            CaptureScreenshot();
        }
        else
        {
            Debug.Log("Currently Processing");
        }
    }

If we are currently processing a screenshot we don’t take another one. This method can be directly called from a button or through another script.

That’s it!

Save this script and attach it to any Gameobject in the scene and configure it.

Please comment below with any queries or thoughts you might have and I will respond ASAP.

Leave a Reply

Your email address will not be published. Required fields are marked *

Join 30 AR projects in 30 days and become a better AR developer
GET FREE LESSONS

Learn AR projects & source code

We shall send you an email with the link to the best starter lesson in 5 minutes
Close
Download source code for this project & get updates of future projects
Download Source Code

Download Source Code

Close
Download source code for this project & get updates of future projects
Download Source Code

Download Source Code

Close
Download source code for this project & get updates of future projects
Download Source Code

Download Source Code

Close
Download source code for this project & get updates of future projects
Download Source Code

Download Source Code

Close
Download source code for this project & get updates of future projects
Download Source Code

Download Source Code

Close
Download source code for this project & get updates of future projects
Download Source Code

Download Source Code

Close
Download source code for this project & get updates of future projects
Download Source Code

Download Source Code

Close
Download source code for this project & get updates of future projects
Download Source Code

Download Source Code

Close
Download source code for this project & get updates of future projects
Download Source Code

Download Source Code

Close
Download source code for this project & get updates of future projects
Download Source Code

Download Source Code

Close
Download source code for this project & get updates of future projects
Download Source Code

Download Source Code

Close
Download source code for this project & get updates of future projects
Download Source Code

Download Source Code

Close
Download source code for this project & get updates of future projects
Download Source Code

Download Source Code

Close
Download source code for this project & get updates of future projects
Download Source Code

Download Source Code

Close
Download source code for this project & get updates of future projects
Download Source Code

Download Source Code

Close