Day 14 : How to take Screenshots within an App using Unity. — Tutorials For AR

Vineet Devaiah
Augmented reality tutorials
4 min readMar 1, 2020

--

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.

Originally published at https://tutorialsforar.com on March 1, 2020.

--

--

Vineet Devaiah
Augmented reality tutorials

CEO@TeliportMe, son and good friend . Stuff said on twitter do not reflect my true opinion but as my stepping stone to standup comedy