Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

export full scene as image

Discussion in 'Scripting' started by dccoo, Sep 19, 2017.

  1. dccoo

    dccoo

    Joined:
    Oct 3, 2015
    Posts:
    161
    I'm building a platform game and I want to export the whole platform (that I see in scene tab) to an image (so I can draw the true graphics over it, instead of the squares and balls I'm using at the moment).

    How can I export the whole 2D scene to an image?
     
  2. LiterallyJeff

    LiterallyJeff

    Joined:
    Jan 21, 2015
    Posts:
    2,807
    Play it maximized and take a screenshot?
     
    aShySir likes this.
  3. Deleted User

    Deleted User

    Guest

  4. dccoo

    dccoo

    Joined:
    Oct 3, 2015
    Posts:
    161
    PS: a full map won't fit in a single conventional screenshot as it is much bigger than the screen
     
  5. Peter77

    Peter77

    QA Jesus

    Joined:
    Jun 12, 2013
    Posts:
    6,589
    You should be able to do this with a editor scripting.

    Here is how my first attempt might look like:
    • Create a temporary ortho Camera and a RenderTexture of size 1024x2014 pixels for example. Assign the RenderTexture as "target" to the Camera.
    • Compute bounds of the entire level. The bounds is a rectangle that encapsulates all objects in the level.
    • Create a texture that is big enough to fit the witdh/height of bounds. This can already become an issue I guess, because if the level is big you might need to allocate a texture with 100000x50000 pixels, which can easily cause to run out of memory or perhaps it's not even supported to create!
    • Let's assume the texture is named "level texture"
    • Move temporary Camera to the top-left bounds
    • Call Camera.Render to render the visible area by the camera to the assigned RenderTexture
    • Copy the RenderTexture to the "level texture", located at the top-left position (x=0, y=0)
    • Move the Camera to the right by the RenderTexture's width
    • Call Camera.Render to render the visible area by the camera to the assigned RenderTexture
    • Copy the RenderTexture to the "level texture", located at (x=RenderTexture.width, y=0)
    • Move the Camera to the further to the right by the RenderTexture's width
    • Call Camera.Render to render the visible area by the camera to the assigned RenderTexture
    • Copy the RenderTexture to the "level texture", located at (x=RenderTexture.width*2, y=0)
    ... and so on. If the camera reached the right most position, you would start the process from the left again, but move it down.

    When this finished, you should have a texture that contains the entire scene content. Now you only need to convert it to a PNG for example and save it to a file.
     
    radiantboy likes this.
  6. hkpnkp

    hkpnkp

    Joined:
    Apr 7, 2017
    Posts:
    2
    genius
     
  7. Eric5h5

    Eric5h5

    Volunteer Moderator Moderator

    Joined:
    Jul 19, 2006
    Posts:
    32,401
    Why would you need to use threads and callbacks? It's entirely procedural.

    --Eric
     
  8. dccoo

    dccoo

    Joined:
    Oct 3, 2015
    Posts:
    161
    the scene is big, so it requires long calculations that freeze unity. I somehow worked it out using coroutines (as threads doesn't communicate with unity), but the result is still worse than I want and I'm still working.
     
  9. radiantboy

    radiantboy

    Joined:
    Nov 21, 2012
    Posts:
    1,633
    Did you ever work this out? I want to do exactly this right now.
     
    Appokalopps likes this.
  10. radiantboy

    radiantboy

    Joined:
    Nov 21, 2012
    Posts:
    1,633
    I just made a panning live camera in the end.
     
  11. SkorpionX

    SkorpionX

    Joined:
    Jul 11, 2016
    Posts:
    3
    I'm probably pretty late with my answer but I needed a script doing exactly that so I used your guide, Peter77, and made myself a proper chunk of code. As I couldn't find any in the web I decided to share it here and hope it will help someone in the future.

    I always try to write self-explaining code but I feel it would be nice to say a few words of introduction.
    • To use my script add a GameObject somewhere on your scene. It's name doesn't matter.
    • Attach the script to the GameObject you created.
    • The script has one field named target. You can attach there certain GameObject that shall be rendered or leave it empty to make the script look for every GameObject on scene that has either any Collider2D or Renderer component. It's because as far as I know only those two implement Bounds. If I'm wrong please correct me (you can add other component types then).
    • The script runs only on start, as it consists of Start() method only. The scene will be rendered and saved to file "as it is".
    • The script was made for 16 Pixels Per Unit (PPU) tilemap based 2D levels primarly so you can encounter some issues trying to render other types of scenes.
    • If you want to adjust the script to your resolution you should know that 160 comes from 16 PPU * 5 (camera's orthographic size) * 2 (as the orthographic size is always only half the screen). I do not recommend changing
      renderCamera.aspect
      property value!
    • You can (and probably will want to) change the file path at the end of the script.
    • Remember to set
      cameraDistance
      so that it has smaller value than rendered objects. Objects with same or greater
      transform.position.z
      (Z position of Editor's Transform) won't be rendered!
    Code (CSharp):
    1. using UnityEngine;
    2.  
    3. public class ScreenShotMaker : MonoBehaviour
    4. {
    5.     public GameObject target;
    6.  
    7.     private RenderTexture renderTexture;
    8.     private Camera renderCamera;
    9.     private Vector4 bounds;
    10.  
    11.     private int resolution = 160;
    12.     private float cameraDistance = -2.0f;
    13.  
    14.     void Start()
    15.     {
    16.         Debug.Log("Initializing camera and stuff...");
    17.      
    18.         gameObject.AddComponent(typeof(Camera));
    19.  
    20.         renderCamera = GetComponent<Camera>();
    21.         renderCamera.enabled = true;
    22.         renderCamera.cameraType = CameraType.Game;
    23.         renderCamera.forceIntoRenderTexture = true;
    24.         renderCamera.orthographic = true;
    25.         renderCamera.orthographicSize = 5;
    26.         renderCamera.aspect = 1.0f;
    27.         renderCamera.targetDisplay = 2;
    28.  
    29.         renderTexture = new RenderTexture(resolution, resolution, 24);
    30.  
    31.         renderCamera.targetTexture = renderTexture;
    32.  
    33.         bounds = new Vector4();
    34.  
    35.         Debug.Log("Initialized successfully!");
    36.         Debug.Log("Computing level boundaries...");
    37.  
    38.         if (target != null)
    39.         {
    40.             Bounds boundaries;
    41.  
    42.             if (target.GetComponentInChildren<Renderer>() != null)
    43.             {
    44.                 boundaries = target.GetComponentInChildren<Renderer>().bounds;
    45.             }
    46.             else if(target.GetComponentInChildren<Collider2D>() != null)
    47.             {
    48.                 boundaries = target.GetComponentInChildren<Collider2D>().bounds;
    49.             }
    50.             else
    51.             {
    52.                 Debug.Log("Unfortunately no boundaries could be found :/");
    53.  
    54.                 return;
    55.             }
    56.          
    57.             bounds.w = boundaries.min.x;
    58.             bounds.x = boundaries.max.x;
    59.             bounds.y = boundaries.min.y;
    60.             bounds.z = boundaries.max.y;
    61.         }
    62.         else
    63.         {
    64.             object[] gameObjects = FindObjectsOfType(typeof(GameObject));
    65.  
    66.             foreach (object gameObj in gameObjects)
    67.             {
    68.                 GameObject levelElement = (GameObject)gameObj;
    69.                 Bounds boundaries = new Bounds();
    70.  
    71.                 if (levelElement.GetComponentInChildren<Renderer>() != null)
    72.                 {
    73.                     boundaries = levelElement.GetComponentInChildren<Renderer>().bounds;
    74.                 }
    75.                 else if (levelElement.GetComponentInChildren<Collider2D>() != null)
    76.                 {
    77.                     boundaries = levelElement.GetComponentInChildren<Collider2D>().bounds;
    78.                 }
    79.                 else
    80.                 {
    81.                     continue;
    82.                 }
    83.  
    84.                 if (boundaries != null)
    85.                 {
    86.                     bounds.w = Mathf.Min(bounds.w, boundaries.min.x);
    87.                     bounds.x = Mathf.Max(bounds.x, boundaries.max.x);
    88.                     bounds.y = Mathf.Min(bounds.y, boundaries.min.y);
    89.                     bounds.z = Mathf.Max(bounds.z, boundaries.max.y);
    90.                 }
    91.             }
    92.         }
    93.      
    94.         Debug.Log("Boundaries computed successfuly! The computed boundaries are " + bounds);
    95.         Debug.Log("Computing target image resolution and final setup...");
    96.  
    97.         int xRes = Mathf.RoundToInt(resolution * ((bounds.x - bounds.w) / (renderCamera.aspect * renderCamera.orthographicSize * 2 * renderCamera.aspect)));
    98.         int yRes = Mathf.RoundToInt(resolution * ((bounds.z - bounds.y) / (renderCamera.aspect * renderCamera.orthographicSize * 2 / renderCamera.aspect)));
    99.  
    100.         Texture2D virtualPhoto = new Texture2D(xRes, yRes, TextureFormat.RGB24, false);
    101.         RenderTexture.active = renderTexture;
    102.  
    103.         Debug.Log("Success! Everything seems ready to render!");
    104.  
    105.         for (float i = bounds.w, xPos = 0; i < bounds.x; i += renderCamera.aspect * renderCamera.orthographicSize * 2, xPos++)
    106.         {
    107.             for (float j = bounds.y, yPos = 0; j < bounds.z; j += renderCamera.aspect * renderCamera.orthographicSize * 2, yPos++)
    108.             {
    109.                 gameObject.transform.position = new Vector3(i + renderCamera.aspect * renderCamera.orthographicSize, j + renderCamera.aspect * renderCamera.orthographicSize, cameraDistance);
    110.  
    111.                 renderCamera.Render();
    112.  
    113.                 virtualPhoto.ReadPixels(new Rect(0, 0, resolution, resolution), (int)xPos * resolution, (int)yPos * resolution);
    114.  
    115.                 Debug.Log("Rendered and copied chunk " + (xPos + 1) + ":" + (yPos + 1));
    116.             }
    117.         }
    118.  
    119.         Debug.Log("All chunks rendered! Some final adjustments and picture should be saved!");
    120.  
    121.         RenderTexture.active = null;
    122.         renderCamera.targetTexture = null;
    123.      
    124.         byte[] bytes = virtualPhoto.EncodeToPNG();
    125.  
    126.         System.IO.File.WriteAllBytes(@"C:\Users\UserName\Pictures\ScreenShotTaken.png", bytes);
    127.  
    128.         Debug.Log("All done! Always happy to help you :)");
    129.     }
    130. }
     
  12. UnityGISTech

    UnityGISTech

    Joined:
    May 6, 2015
    Posts:
    193
    Hi there
    Is that scripte can help me to make screenshot with ortho camera to a large terrain (50000x80000)?
     
  13. SkorpionX

    SkorpionX

    Joined:
    Jul 11, 2016
    Posts:
    3
    Yes, as long as you have any objects containing any types of renderers or colliders.

    I might add a functionality to set custom world coordinations too in the future (no time right now).
     
  14. UnityGISTech

    UnityGISTech

    Joined:
    May 6, 2015
    Posts:
    193
    thanks i will try it.
     
  15. Schumaker

    Schumaker

    Joined:
    Jul 14, 2016
    Posts:
    40
    this is first code a get here and works properly without change anything, thank you !!!
     
  16. andreiagmu

    andreiagmu

    Joined:
    Feb 20, 2014
    Posts:
    175
    I made improvements to @SkorpionX's code. Thank you very much for sharing your original code! :D
    I added new fields and changed some stuff in the code, to avoid exceptions and to add some QoL improvements.

    - Now you can set the Background Type and Background Color of the resulting screenshot.
    - The screenshot will be saved on the user's Desktop folder (I only tested on Windows, but I think it will work on any OS).
    - The exported file's name will also have the current DateTime appended to it (which can optionally use UTC time), so you can take multiple screenshots without overwriting the last one. ;)
    The resulting filename is like this example: (format "yyyy-MM-dd-HH-mm-ss")
    ScreenShotTaken_2022-06-27-13-41-17.png

    You can customize the file path/filename/DateTime format by editing the code.

    I also added an Odin Inspector button to take the screenshot at any time in the inspector, be it while running Play Mode or outside Play Mode.
    You need to have Odin Inspector installed in your project for this "Take Screenshot" button to appear. But the script won't cause compilation issues if you don't have Odin Inspector installed.
    If you don't have Odin, you can always add a standard UGUI or IMGUI Button to your game and call
    TakeScreenshot()
    from there. :)

    capture_20220627_104627_001.png

    Code (CSharp):
    1. using System;
    2. using System.IO;
    3. using UnityEngine;
    4.  
    5. #if ODIN_INSPECTOR
    6. using Sirenix.OdinInspector;
    7. #endif
    8.  
    9. public class ScreenShotMaker : MonoBehaviour
    10. {
    11.     public GameObject target;
    12.     public int resolution = 160;
    13.     public float cameraDistance = -2.0f;
    14.     public CameraClearFlags backgroundType = CameraClearFlags.SolidColor;
    15.     public Color backgroundColor = new Color(0.1921569f, 0.3019608f, 0.4745098f, 0f);
    16.     public bool useUTCDateTime;
    17.  
    18.     private RenderTexture renderTexture;
    19.     private Camera renderCamera;
    20.     private Vector4 bounds;
    21.  
    22.     private void Start()
    23.     {
    24.         // If you want to take screenshot when the scene starts, uncomment the next line.
    25.         //TakeScreenshot();
    26.     }
    27.  
    28. #if ODIN_INSPECTOR
    29.     [Button]
    30. #endif
    31.     public void TakeScreenshot()
    32.     {
    33.         ProcessScreenshot();
    34.     }
    35.  
    36.     private void ProcessScreenshot()
    37.     {
    38.         Debug.Log("Initializing camera and stuff...");
    39.  
    40.         if (!GetComponent<Camera>())
    41.             gameObject.AddComponent<Camera>();
    42.  
    43.         renderCamera = GetComponent<Camera>();
    44.         renderCamera.enabled = true;
    45.         renderCamera.cameraType = CameraType.Game;
    46.         renderCamera.forceIntoRenderTexture = true;
    47.         renderCamera.orthographic = true;
    48.         renderCamera.orthographicSize = 5;
    49.         renderCamera.aspect = 1.0f;
    50.         renderCamera.targetDisplay = 2;
    51.         renderCamera.clearFlags = backgroundType;
    52.         renderCamera.backgroundColor = backgroundColor;
    53.  
    54.         renderTexture = new RenderTexture(resolution, resolution, 24);
    55.  
    56.         renderCamera.targetTexture = renderTexture;
    57.  
    58.         bounds = new Vector4();
    59.  
    60.         Debug.Log("Initialized successfully!");
    61.         Debug.Log("Computing level boundaries...");
    62.  
    63.         if (target != null)
    64.         {
    65.             Bounds boundaries;
    66.  
    67.             if (target.GetComponentInChildren<Renderer>() != null)
    68.             {
    69.                 boundaries = target.GetComponentInChildren<Renderer>().bounds;
    70.             }
    71.             else if(target.GetComponentInChildren<Collider2D>() != null)
    72.             {
    73.                 boundaries = target.GetComponentInChildren<Collider2D>().bounds;
    74.             }
    75.             else
    76.             {
    77.                 Debug.Log("Unfortunately no boundaries could be found :/");
    78.                 return;
    79.             }
    80.      
    81.             bounds.w = boundaries.min.x;
    82.             bounds.x = boundaries.max.x;
    83.             bounds.y = boundaries.min.y;
    84.             bounds.z = boundaries.max.y;
    85.         }
    86.         else
    87.         {
    88.             object[] gameObjects = FindObjectsOfType(typeof(GameObject));
    89.  
    90.             foreach (object gameObj in gameObjects)
    91.             {
    92.                 GameObject levelElement = (GameObject)gameObj;
    93.                 Bounds boundaries = new Bounds();
    94.  
    95.                 if (levelElement.GetComponentInChildren<Renderer>() != null)
    96.                 {
    97.                     boundaries = levelElement.GetComponentInChildren<Renderer>().bounds;
    98.                 }
    99.                 else if (levelElement.GetComponentInChildren<Collider2D>() != null)
    100.                 {
    101.                     boundaries = levelElement.GetComponentInChildren<Collider2D>().bounds;
    102.                 }
    103.                 else
    104.                 {
    105.                     continue;
    106.                 }
    107.  
    108.                 if (boundaries != null)
    109.                 {
    110.                     bounds.w = Mathf.Min(bounds.w, boundaries.min.x);
    111.                     bounds.x = Mathf.Max(bounds.x, boundaries.max.x);
    112.                     bounds.y = Mathf.Min(bounds.y, boundaries.min.y);
    113.                     bounds.z = Mathf.Max(bounds.z, boundaries.max.y);
    114.                 }
    115.             }
    116.         }
    117.  
    118.         Debug.Log("Boundaries computed successfuly! The computed boundaries are " + bounds);
    119.         Debug.Log("Computing target image resolution and final setup...");
    120.  
    121.         int xRes = Mathf.RoundToInt(resolution * ((bounds.x - bounds.w) / (renderCamera.aspect * renderCamera.orthographicSize * 2 * renderCamera.aspect)));
    122.         int yRes = Mathf.RoundToInt(resolution * ((bounds.z - bounds.y) / (renderCamera.aspect * renderCamera.orthographicSize * 2 / renderCamera.aspect)));
    123.  
    124.         Texture2D virtualPhoto = new Texture2D(xRes, yRes, TextureFormat.RGB24, false);
    125.         RenderTexture.active = renderTexture;
    126.  
    127.         Debug.Log("Success! Everything seems ready to render!");
    128.  
    129.         for (float i = bounds.w, xPos = 0; i < bounds.x; i += renderCamera.aspect * renderCamera.orthographicSize * 2, xPos++)
    130.         {
    131.             for (float j = bounds.y, yPos = 0; j < bounds.z; j += renderCamera.aspect * renderCamera.orthographicSize * 2, yPos++)
    132.             {
    133.                 gameObject.transform.position = new Vector3(i + renderCamera.aspect * renderCamera.orthographicSize, j + renderCamera.aspect * renderCamera.orthographicSize, cameraDistance);
    134.  
    135.                 renderCamera.Render();
    136.  
    137.                 virtualPhoto.ReadPixels(new Rect(0, 0, resolution, resolution), (int)xPos * resolution, (int)yPos * resolution);
    138.  
    139.                 Debug.Log("Rendered and copied chunk " + (xPos + 1) + ":" + (yPos + 1));
    140.             }
    141.         }
    142.  
    143.         Debug.Log("All chunks rendered! Some final adjustments and picture should be saved!");
    144.  
    145.         RenderTexture.active = null;
    146.         renderCamera.targetTexture = null;
    147.         renderCamera.enabled = false;
    148.         transform.position = Vector3.zero;
    149.  
    150.         byte[] bytes = virtualPhoto.EncodeToPNG();
    151.  
    152.         var dateTimeNow = useUTCDateTime ? DateTime.UtcNow : DateTime.Now;
    153.      
    154.         File.WriteAllBytes($"{Environment.GetFolderPath(Environment.SpecialFolder.Desktop)}" +
    155.                            $"{Path.DirectorySeparatorChar}ScreenShotTaken_{dateTimeNow.ToString("yyyy-MM-dd-HH-mm-ss")}.png", bytes);
    156.  
    157.         Debug.Log("All done! Always happy to help you :)");
    158.     }
    159. }
    160.  
     
    Last edited: Jun 27, 2022
    DavidTeo likes this.
  17. mahmoud_anwar

    mahmoud_anwar

    Joined:
    Aug 1, 2022
    Posts:
    18
    I see the path is on computer. What if I want to save it to mobile phone memory where my app is running?
     
  18. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,514
    Mobile phones have filesystems too. You can use Application.persistentDataPath to create a filename to store it.

    If you want it in the mobile phone's album or photo storage, look online for tutorials or else check out asset store packages.

    If you want to email it to yourself, similarly check out some tutorials for attaching photos to emails.

    ----------------------------

    Tutorials and example code are great, but keep this in mind to maximize your success and minimize your frustration:

    How to do tutorials properly, two (2) simple steps to success:

    Step 1. Follow the tutorial and do every single step of the tutorial 100% precisely the way it is shown. Even the slightest deviation (even a single character!) generally ends in disaster. That's how software engineering works. Every step must be taken, every single letter must be spelled, capitalized, punctuated and spaced (or not spaced) properly, literally NOTHING can be omitted or skipped.

    Fortunately this is the easiest part to get right: Be a robot. Don't make any mistakes.
    BE PERFECT IN EVERYTHING YOU DO HERE!!

    If you get any errors, learn how to read the error code and fix your error. Google is your friend here. Do NOT continue until you fix your error. Your error will probably be somewhere near the parenthesis numbers (line and character position) in the file. It is almost CERTAINLY your typo causing the error, so look again and fix it.

    Step 2. Go back and work through every part of the tutorial again, and this time explain it to your doggie. See how I am doing that in my avatar picture? If you have no dog, explain it to your house plant. If you are unable to explain any part of it, STOP. DO NOT PROCEED. Now go learn how that part works. Read the documentation on the functions involved. Go back to the tutorial and try to figure out WHY they did that. This is the part that takes a LOT of time when you are new. It might take days or weeks to work through a single 5-minute tutorial. Stick with it. You will learn.

    Step 2 is the part everybody seems to miss. Without Step 2 you are simply a code-typing monkey and outside of the specific tutorial you did, you will be completely lost. If you want to learn, you MUST do Step 2.

    Of course, all this presupposes no errors in the tutorial. For certain tutorial makers (like Unity, Brackeys, Imphenzia, Sebastian Lague) this is usually the case. For some other less-well-known content creators, this is less true. Read the comments on the video: did anyone have issues like you did? If there's an error, you will NEVER be the first guy to find it.
     
  19. Bloothec

    Bloothec

    Joined:
    Apr 11, 2019
    Posts:
    1

    hi, when i set the backgroundtype to nothing it actually gives me a black background :( do u have any idea what could be the problem?
     
  20. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,514
    Please don't necro-post. When you have a problem, start your own post... it's FREE!

    When you post, remember we cannot read your mind and we cannot see your screen.

    How to report your problem productively in the Unity3D forums:

    http://plbm.com/?p=220

    This is the bare minimum of information to report:

    - what you want
    - what you tried
    - what you expected to happen
    - what actually happened, log output, variable values, and especially any errors you see
    - links to documentation you used to cross-check your work (CRITICAL!!!)

    If you post a code snippet, ALWAYS USE CODE TAGS:

    How to use code tags: https://forum.unity.com/threads/using-code-tags-properly.143875/