5
\$\begingroup\$

How can I screenshot particular UI elements?

In my case I need screen capture UI Panel with child objects like UI Text, UI Input and etc.

Please take a look at picture. Thanks in advance

enter image description here

\$\endgroup\$
6
\$\begingroup\$
using UnityEngine;
using System.Collections;
using System.IO;

public class ScreenshotNow : MonoBehaviour 
{
public RectTransform rectT; // Assign the UI element which you wanna capture
int width; // width of the object to capture
int height; // height of the object to capture

// Use this for initialization
void Start () 
{
    width = System.Convert.ToInt32(rectT.rect.width); 
    height = System.Convert.ToInt32(rectT.rect.height);
}

// Update is called once per frame
void Update () 
{
    if (Input.GetMouseButtonDown (0)) 
    {
        StartCoroutine(takeScreenShot ()); // screenshot of a particular UI Element.
    }
    if (Input.GetKeyDown (KeyCode.A)) 
    {
        Application.CaptureScreenshot ("FullPageScreenShot.png");
    }

}
public IEnumerator takeScreenShot()
{
    yield return new WaitForEndOfFrame (); // it must be a coroutine 

    Vector2 temp = rectT.transform.position;
    var startX = temp.x - width/2;
    var startY = temp.y - height/2;

    var tex = new Texture2D (width, height, TextureFormat.RGB24, false);
    tex.ReadPixels (new Rect(startX, startY, width, height), 0, 0);
    tex.Apply ();

    // Encode texture into PNG
    var bytes = tex.EncodeToPNG();
    Destroy(tex);

    File.WriteAllBytes(Application.dataPath + "ScreenShot.png", bytes);

}
}
\$\endgroup\$
  • \$\begingroup\$ It captures my Panel differently evety time when I'm changing the size of game window. So, its unstable now. And in full size windows it captures only a part of UI Panel \$\endgroup\$ – Nurlan Shukurov Feb 12 '16 at 7:56
  • \$\begingroup\$ I just cheked it in my Unity editor with different landscape and portrait resolutions and also in maximize screen. It worked fine. have u assigned the "rectT" in the inspector \$\endgroup\$ – Shuvro Sarkar Feb 12 '16 at 8:01
  • \$\begingroup\$ Yes, assigned. Please take a look imgur.com/se18het \$\endgroup\$ – Nurlan Shukurov Feb 12 '16 at 8:08
  • \$\begingroup\$ Check the byte to sprite converter scripts. I think there might be a problem. Here is my script I made for you. Try this, let me know if the problem still exits. gist.github.com/Shubhra22/bab1052cd90b9f4b89b3 \$\endgroup\$ – Shuvro Sarkar Feb 12 '16 at 8:48
  • \$\begingroup\$ youtube.com/watch?v=VdJqUj7ucQU \$\endgroup\$ – Nurlan Shukurov Feb 12 '16 at 9:07
0
\$\begingroup\$

In my runs, I discovered the solution to the size of the window and only part of the panel problem was solved by going to the Canvas Scaler and changing UI Scale mode to costant pixel.

\$\endgroup\$
0
\$\begingroup\$

This is the modified answer for scaleWithScreenSize option on CanvasScaler,

    if(sourceRect.root.GetComponent<UnityEngine.UI.CanvasScaler>().uiScaleMode == UnityEngine.UI.CanvasScaler.ScaleMode.ScaleWithScreenSize)
    {
        UnityEngine.UI.CanvasScaler scaler = sourceRect.root.GetComponent<UnityEngine.UI.CanvasScaler>();
        if (scaler.matchWidthOrHeight == 0)
        {
            height = System.Convert.ToInt32((float)Screen.width  / (float)width * height);
            width = Screen.width;
        }
        else // scaler.matchWidthOrHeight == 1
        {
            width = System.Convert.ToInt32((float)Screen.height / (float)height * width);
            height = Screen.height;
        }
    }

Just a quick fix for the matchWidthOrHeight on 0 and 1, If anyone have some idea about the value between, please let me know :)

(Add code below Vector2 temp = rectT.transform.position; from Shuvro Sarkar's answer)

\$\endgroup\$
0
\$\begingroup\$

I was able to accomplish this even with CanvasScaler set to scaleWithScreenSize and matchWidthOrHeight set to arbitrary value, using the RectTransformUtility api:

    Rect rect = RectTransformUtility.PixelAdjustRect(rectT, canvas);

    int textWidth = System.Convert.ToInt32(rect.width); // width of the object to capture
    int textHeight = System.Convert.ToInt32(rect.height); // height of the object to capture

    var startX = System.Convert.ToInt32(rect.x) + Screen.width / 2; // offset X
    var startY = System.Convert.ToInt32(rect.y) + Screen.height / 2; // offset Y

    var tex = new Texture2D(textWidth, textHeight, TextureFormat.RGB24, false);
    tex.ReadPixels(new Rect(startX, startY, textWidth, textHeight), 0, 0);
    tex.Apply();
\$\endgroup\$
0
\$\begingroup\$

As per the Williams answer, i'm created the dynamic canvas with scalar mode set to constant pixel and able to take screenshot of my desired recttransform.

public class ScreenShotRect : MonoBehaviour {

    public RectTransform rectT; // Assign the UI element which you wanna capture

    private int width; // width of the object to capture
    private int height; // height of the object to capture
    private Transform rectTransformParent;  //RectTransform parent to reasign after usage
    private GameObject screenShotGO;    //Tempory GameObject to copy recttransform
                                        //RectTransform values to reasign after usage
    private Vector2 offsetMinValues;
    private Vector2 offsetMaxValues;
    private Vector3 localScaleValues;

    // Update is called once per frame
    void Update()
    {
        if (Input.GetKeyDown(KeyCode.Space))
        {
            if (rectT != null)
            {
                if (rectT.root.GetComponent<CanvasScaler>().uiScaleMode == CanvasScaler.ScaleMode.ScaleWithScreenSize)
                {
                    if (rectT.root.GetComponent<CanvasScaler>().screenMatchMode != CanvasScaler.ScreenMatchMode.MatchWidthOrHeight || 
                        rectT.root.GetComponent<CanvasScaler>().matchWidthOrHeight != 0.5f)
                    { 
                            Debug.LogWarning("UI may not look the same due to Canvas Scaler either screenMatchMode was not set MatchWidthOrHeight or MatchWidthOrHeight is not set to 0.5f");
                    }
                    createCanvasWithRectTransform();
                }
                else if (rectT.root.GetComponent<CanvasScaler>().uiScaleMode == CanvasScaler.ScaleMode.ConstantPixelSize)
                {
                    StartCoroutine(takeScreenShot());
                }
                else
                {
                    Debug.LogWarning("Canvas Scaler mode not supported.");
                }
            }
            else
            {
                Debug.Log("Rect transform is null to capture the screenshot, hence fullscreen has been taken.");
                Application.CaptureScreenshot("FullPageScreenShot.png");
            }
        }

    }

    private void createCanvasWithRectTransform()
    {
        rectTransformParent = rectT.parent; //Assigning Parent transform to reasign after usage

        //Copying RectTransform values to reasign after switching parent
        offsetMinValues = rectT.offsetMin;
        offsetMaxValues = rectT.offsetMax;
        localScaleValues = rectT.localScale;

        //Creating secondary CANVAS with required fields
        screenShotGO = new GameObject("ScreenShotGO");
        screenShotGO.transform.parent = null;
        Canvas canvas = screenShotGO.AddComponent<Canvas>();
        canvas.renderMode = RenderMode.ScreenSpaceOverlay;
        CanvasScaler canvasScalar = screenShotGO.AddComponent<CanvasScaler>();
        canvasScalar.uiScaleMode = CanvasScaler.ScaleMode.ConstantPixelSize;
        screenShotGO.AddComponent<GraphicRaycaster>();

        rectT.SetParent(screenShotGO.transform);    //Assigning capture recttransform to temporary parent gameobject

        //Reasigning recttransform values
        rectT.offsetMin = offsetMinValues;
        rectT.offsetMax = offsetMaxValues;
        rectT.localScale = localScaleValues;

        Canvas.ForceUpdateCanvases();   // Forcing all canvas to update the UI

        StartCoroutine(takeScreenShot());   //Once everything was set ready, Capture the screenshot
    }

    private IEnumerator takeScreenShot()
    {
        yield return new WaitForEndOfFrame(); // it must be a coroutine 

        //Calcualtion for the width and height of the screenshot from recttransform
        width = System.Convert.ToInt32(rectT.rect.width);
        height = System.Convert.ToInt32(rectT.rect.height);

        //Calcualtion for the starting position of the recttransform to be captured
        Vector2 temp = rectT.transform.position;
        var startX = temp.x - width / 2;
        var startY = temp.y - height / 2;

        // Read the pixels from the texture
        var tex = new Texture2D(width, height, TextureFormat.RGB24, false);
        tex.ReadPixels(new Rect(startX, startY, width, height), 0, 0);
        tex.Apply();

        // split the process up--ReadPixels() and the GetPixels() call inside of the encoder are both pretty heavy
        yield return 0;

        var bytes = tex.EncodeToPNG();
        Destroy(tex);

        //Writing bytes to a file
        File.WriteAllBytes(Application.dataPath + "/ScreenShotRect/ScreenShot.png", bytes);

        //In case of ScaleMode was not ScaleWithScreenSize, parent will not be assigned then no need to revert the changes
        if (rectTransformParent != null)
        {
            //Reasigning gameobject to its original parent group
            rectT.SetParent(rectTransformParent);

            //Reasigning recttransform values
            rectT.offsetMin = offsetMinValues;
            rectT.offsetMax = offsetMaxValues;
            rectT.localScale = localScaleValues;

            //Destorying temporary created gameobject after usage
            Destroy(screenShotGO);
        }

        Debug.Log("Picture taken");
    }
}

Note: UI may not look the same when Canvas Scaler either screenMatchMode was not set MatchWidthOrHeight or MatchWidthOrHeight is not set to 0.5f

This code is modified based on Shuvro Sarkar's answer

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.