10

Unity.UI について考えてみてくださいCanvas

Panelそのキャンバスの典型を想像してみてください。たまたまいくつかの画像、おそらくいくつかのテキストなどが含まれているとします。

そのパネル (パネルのみ) をスクリーンショット (Texture2D または PNG) に変換できると非常に便利です。

私が考えることができる唯一のことは、画面上の問題ReadPixelsの領域を使用して把握することです(実際にはかなりトリッキーです)。Panelこれは、パネルがたまたま正方形で、斜めに回転していない場合にのみ機能します。

1 つのパネル、または少なくとも 1 つのキャンバス全体をレンダリングする方法があるはずだと思いますか? 何も見つかりません。

ここに画像の説明を入力

ここに画像の説明を入力

この例では、ピンクの Panel を PNG 画像にします。ああ。

(もちろん、1 つの Panel ではなく、単に「Canvas 全体」を処理するソリューションを誰かが持っている場合は、もちろんそれも立派です。)

4

3 に答える 3

11

以下のコードは Canvas の写真を撮ることができます。Canvas は、渡すオブジェクトにアタッチする必要があります。呼び出す唯一の関数は void takeScreenShot(Canvas canvasPanel, SCREENSHOT_TYPE screenShotType = SCREENSHOT_TYPE.IMAGE_AND_TEXT, bool createNewInstance = true)

SCREENSHOT_TYPE.IMAGE_AND_TEXTパラメータは画像テキストの写真を撮ります。

SCREENSHOT_TYPE.IMAGE_ONLYパラメータは画像の写真のみを撮ります。画面上のすべてのテキストが除外されます。セキュリティ上の理由から、これを使用してテキストを削除し、グラフィックのみを表示できます。

SCREENSHOT_TYPE.TEXT_ONLYパラメータはテキストのみを撮影します。

使い方。GameObject を作成し、CanvasScreenShot スクリプトをアタッチします。; に登録してからCanvasScreenShot.OnPictureTaken(byte[] pngArray)呼び出すscreenShot.takeScreenShot(canvasToSreenShot, SCREENSHOT_TYPE.IMAGE_AND_TEXT, false);

完全なコード:

あなたのtest.csスクリプト:

public class test : MonoBehaviour
{
    public Canvas canvasToSreenShot;

    // Use this for initialization
    void Start()
    {
        //Subscribe
        CanvasScreenShot.OnPictureTaken += receivePNGScreenShot;
        CanvasScreenShot screenShot = GameObject.Find("GameObject").GetComponent<CanvasScreenShot>();

        //take ScreenShot(Image and Text)
        //screenShot.takeScreenShot(canvasToSreenShot, SCREENSHOT_TYPE.IMAGE_AND_TEXT, false);
        //take ScreenShot(Image only)
        screenShot.takeScreenShot(canvasToSreenShot, SCREENSHOT_TYPE.IMAGE_ONLY, false);
        //take ScreenShot(Text only)
        // screenShot.takeScreenShot(canvasToSreenShot, SCREENSHOT_TYPE.TEXT_ONLY, false);

    }

    public void OnEnable()
    {
        //Un-Subscribe
        CanvasScreenShot.OnPictureTaken -= receivePNGScreenShot;
    }

    void receivePNGScreenShot(byte[] pngArray)
    {
        Debug.Log("Picture taken");

        //Do Something With the Image (Save)
        string path = Application.persistentDataPath + "/CanvasScreenShot.png";
        System.IO.File.WriteAllBytes(path, pngArray);
        Debug.Log(path);
    }

}

CanvasScreenShot.csスクリプト:

public class CanvasScreenShot : MonoBehaviour
{
    /*
 CanvasScreenShot by programmer.
 http://stackoverflow.com/questions/36555521/unity3d-build-png-from-panel-of-a-unity-ui#36555521
 http://stackoverflow.com/users/3785314/programmer
 */

    //Events
    public delegate void takePictureHandler(byte[] pngArray);
    public static event takePictureHandler OnPictureTaken;

    private GameObject duplicatedTargetUI;
    private Image[] allImages;
    private Text[] allTexts;

    //Store all other canvas that will be disabled and re-anabled after screenShot
    private Canvas[] allOtherCanvas;

    //takes Screenshot
    public void takeScreenShot(Canvas canvasPanel, SCREENSHOT_TYPE screenShotType = SCREENSHOT_TYPE.IMAGE_AND_TEXT, bool createNewInstance = true)
    {
        StartCoroutine(_takeScreenShot(canvasPanel, screenShotType, createNewInstance));
    }

    private IEnumerator _takeScreenShot(Canvas canvasPanel, SCREENSHOT_TYPE screenShotType = SCREENSHOT_TYPE.IMAGE_AND_TEXT, bool createNewInstance = true)
    {
        //Get Visible Canvas In the Scene
        allOtherCanvas = getAllCanvasInScene(false);

        //Hide all the other Visible Canvas except the one that is passed in as parameter(Canvas we want to take Picture of)
        showCanvasExcept(allOtherCanvas, canvasPanel, false);
        //Reset the position so that both UI will be in the-same place if we make the duplicate a child
        resetPosAndRot(gameObject);

        //Check if we should operate on the original image or make a duplicate of it
        if (createNewInstance)
        {
            //Duplicate the Canvas we want to take Picture of
            duplicatedTargetUI = duplicateUI(canvasPanel.gameObject, "ScreenShotUI");
            //Make this game object the parent of the Canvas
            duplicatedTargetUI.transform.SetParent(gameObject.transform);

            //Hide the orginal Canvas we want to take Picture of
            showCanvas(canvasPanel, false);
        }
        else
        {
            //No duplicate. Use original GameObject
            //Make this game object the parent of the Canvas
            canvasPanel.transform.SetParent(gameObject.transform);
        }

        RenderMode defaultRenderMode;

        //Change the duplicated Canvas to RenderMode to overlay
        Canvas duplicatedCanvas = null;
        if (createNewInstance)
        {
            duplicatedCanvas = duplicatedTargetUI.GetComponent<Canvas>();
            defaultRenderMode = duplicatedCanvas.renderMode;
            duplicatedCanvas.renderMode = RenderMode.ScreenSpaceOverlay;
        }
        else
        {
            defaultRenderMode = canvasPanel.renderMode;
            canvasPanel.renderMode = RenderMode.ScreenSpaceOverlay;
        }


        if (screenShotType == SCREENSHOT_TYPE.IMAGE_AND_TEXT)
        {
            //No Action Needed
        }
        else if (screenShotType == SCREENSHOT_TYPE.IMAGE_ONLY)
        {
            if (createNewInstance)
            {
                //Get all images on the duplicated visible Canvas
                allTexts = getAllTextsFromCanvas(duplicatedTargetUI, false);
                //Hide those images
                showTexts(allTexts, false);
            }
            else
            {
                //Get all images on the duplicated visible Canvas
                allTexts = getAllTextsFromCanvas(canvasPanel.gameObject, false);
                //Hide those images
                showTexts(allTexts, false);
            }
        }
        else if (screenShotType == SCREENSHOT_TYPE.TEXT_ONLY)
        {
            if (createNewInstance)
            {
                //Get all images on the duplicated visible Canvas
                allImages = getAllImagesFromCanvas(duplicatedTargetUI, false);
                //Hide those images
                showImages(allImages, false);
            }
            else
            {
                //Get all images on the duplicated visible Canvas
                allImages = getAllImagesFromCanvas(canvasPanel.gameObject, false);
                //Hide those images
                showImages(allImages, false);
            }
        }

        //////////////////////////////////////Finally Take ScreenShot///////////////////////////////
        yield return new WaitForEndOfFrame();
        Texture2D screenImage = new Texture2D(Screen.width, Screen.height);
        //Get Image from screen
        screenImage.ReadPixels(new Rect(0, 0, Screen.width, Screen.height), 0, 0);
        screenImage.Apply();

        //Convert to png
        byte[] pngBytes = screenImage.EncodeToPNG();

        /*FOR TESTING/DEBUGGING PURPOSES ONLY. COMMENT THIS
        string path = Application.persistentDataPath + "/CanvasScreenShot.png";
        System.IO.File.WriteAllBytes(path, pngBytes);
        Debug.Log(path);*/

        //Notify functions that are subscribed to this event that picture is taken then pass in image bytes as png
        if (OnPictureTaken != null)
        {
            OnPictureTaken(pngBytes);
        }


        ///////////////////////////////////RE-ENABLE OBJECTS

        //Change the duplicated Canvas RenderMode back to default Value
        if (createNewInstance)
        {
            duplicatedCanvas.renderMode = defaultRenderMode;
        }
        else
        {
            canvasPanel.renderMode = defaultRenderMode;
        }
        //Un-Hide all the other Visible Canvas except the one that is passed in as parameter(Canvas we want to take Picture of)
        showCanvas(allOtherCanvas, true);
        if (screenShotType == SCREENSHOT_TYPE.IMAGE_AND_TEXT)
        {
            //No Action Needed
        }
        else if (screenShotType == SCREENSHOT_TYPE.IMAGE_ONLY)
        {
            //Un-Hide those images
            showTexts(allTexts, true);
        }
        else if (screenShotType == SCREENSHOT_TYPE.TEXT_ONLY)
        {
            //Un-Hide those images
            showImages(allImages, true);
        }

        //Un-hide the orginal Canvas we want to take Picture of
        showCanvas(canvasPanel, true);

        if (createNewInstance)
        {
            //Destroy the duplicated GameObject
            Destroy(duplicatedTargetUI, 1f);
        }
        else
        {
            //Remove the Canvas as parent 
            canvasPanel.transform.SetParent(null);
        }
    }

    private GameObject duplicateUI(GameObject parentUICanvasOrPanel, string newOBjectName)
    {
        GameObject tempObj = Instantiate(parentUICanvasOrPanel);
        tempObj.name = newOBjectName;
        return tempObj;
    }


    private Image[] getAllImagesFromCanvas(GameObject canvasParentGameObject, bool findDisabledCanvas = false)
    {
        Image[] tempImg = canvasParentGameObject.GetComponentsInChildren<Image>(findDisabledCanvas);
        if (findDisabledCanvas)
        {
            return tempImg;
        }
        else
        {
            System.Collections.Generic.List<Image> canvasList = new System.Collections.Generic.List<Image>();
            for (int i = 0; i < tempImg.Length; i++)
            {
                if (tempImg[i].enabled)
                {
                    canvasList.Add(tempImg[i]);
                }
            }
            return canvasList.ToArray();
        }
    }

    private Text[] getAllTextsFromCanvas(GameObject canvasParentGameObject, bool findDisabledCanvas = false)
    {
        Text[] tempImg = canvasParentGameObject.GetComponentsInChildren<Text>(findDisabledCanvas);
        if (findDisabledCanvas)
        {
            return tempImg;
        }
        else
        {
            System.Collections.Generic.List<Text> canvasList = new System.Collections.Generic.List<Text>();
            for (int i = 0; i < tempImg.Length; i++)
            {
                if (tempImg[i].enabled)
                {
                    canvasList.Add(tempImg[i]);
                }
            }
            return canvasList.ToArray();
        }
    }

    private Canvas[] getAllCanvasFromCanvas(Canvas canvasParentGameObject, bool findDisabledCanvas = false)
    {
        Canvas[] tempImg = canvasParentGameObject.GetComponentsInChildren<Canvas>(findDisabledCanvas);
        if (findDisabledCanvas)
        {
            return tempImg;
        }
        else
        {
            System.Collections.Generic.List<Canvas> canvasList = new System.Collections.Generic.List<Canvas>();
            for (int i = 0; i < tempImg.Length; i++)
            {
                if (tempImg[i].enabled)
                {
                    canvasList.Add(tempImg[i]);
                }
            }
            return canvasList.ToArray();
        }
    }

    //Find Canvas.
    private Canvas[] getAllCanvasInScene(bool findDisabledCanvas = false)
    {
        Canvas[] tempCanvas = GameObject.FindObjectsOfType<Canvas>();
        if (findDisabledCanvas)
        {
            return tempCanvas;
        }
        else
        {
            System.Collections.Generic.List<Canvas> canvasList = new System.Collections.Generic.List<Canvas>();
            for (int i = 0; i < tempCanvas.Length; i++)
            {
                if (tempCanvas[i].enabled)
                {
                    canvasList.Add(tempCanvas[i]);
                }
            }
            return canvasList.ToArray();
        }
    }

    //Disable/Enable Images
    private void showImages(Image[] imagesToDisable, bool enableImage = true)
    {
        for (int i = 0; i < imagesToDisable.Length; i++)
        {
            imagesToDisable[i].enabled = enableImage;
        }
    }

    //Disable/Enable Texts
    private void showTexts(Text[] imagesToDisable, bool enableTexts = true)
    {
        for (int i = 0; i < imagesToDisable.Length; i++)
        {
            imagesToDisable[i].enabled = enableTexts;
        }
    }


    //Disable/Enable Canvas
    private void showCanvas(Canvas[] canvasToDisable, bool enableCanvas = true)
    {
        for (int i = 0; i < canvasToDisable.Length; i++)
        {
            canvasToDisable[i].enabled = enableCanvas;
        }
    }


    //Disable/Enable one canvas
    private void showCanvas(Canvas canvasToDisable, bool enableCanvas = true)
    {
        canvasToDisable.enabled = enableCanvas;
    }

    //Disable/Enable Canvas Except
    private void showCanvasExcept(Canvas[] canvasToDisable, Canvas ignoreCanvas, bool enableCanvas = true)
    {
        for (int i = 0; i < canvasToDisable.Length; i++)
        {
            if (!(canvasToDisable[i] == ignoreCanvas))
            {
                canvasToDisable[i].enabled = enableCanvas;
            }
        }
    }

    //Disable/Enable Canvas Except
    private void showCanvasExcept(Canvas[] canvasToDisable, Canvas[] ignoreCanvas, bool enableCanvas = true)
    {
        for (int i = 0; i < canvasToDisable.Length; i++)
        {
            for (int j = 0; j < ignoreCanvas.Length; j++)
            {
                if (!(canvasToDisable[i] == ignoreCanvas[j]))
                {
                    canvasToDisable[i].enabled = enableCanvas;
                }
            }
        }
    }

    //Reset Position
    private void resetPosAndRot(GameObject posToReset)
    {
        posToReset.transform.position = Vector3.zero;
        posToReset.transform.rotation = Quaternion.Euler(Vector3.zero);
    }

}

public enum SCREENSHOT_TYPE
{
    IMAGE_AND_TEXT, IMAGE_ONLY, TEXT_ONLY
}
于 2016-04-12T20:39:08.873 に答える
1

深く調べたことはありませんが、親 Canvas をレンダリングせずに Panel をレンダリングすることは不可能だと思います。また、これをフレームごとに行うのではなく、特定の機会にのみ行うことも想定しています。

その文脈で、ここに私が試みることがあります:

  • RenderMode "Screen Space - Camera" で 2 つ目の Canvas を作成します。これにより、この Canvas のレンダリングに使用されるカメラを指定できます。
  • 2 番目のキャンバスをレンダリングするための専用カメラを用意します。
  • OnPreCull と OnPostRender を処理するスクリプトをカメラに与える
  • OnPreCull で、ターゲット Panel をセカンダリ Canvas にアタッチします。OnPostRender、元の場所に貼り直してください (申し訳ありません)
  • セカンダリ カメラを RenderTexture にレンダリングさせる
  • ReadPixels、Apply、EncodeToPNG、出来上がり

もちろん、サイズや位置など、指定されていない詳細がたくさんあります。しかし、専用の Canvas と Camera を使用すると、これらすべてを把握して、混乱させることなく正しくセットアップできるはずです。

于 2016-04-12T11:58:02.700 に答える
1

公式の簡単な方法が見つからないので、これは簡単な方法です。

試したことがないので、これがどれほどパフォーマンスに影響するかはわかりません。

カメラを追加し、UI パネルに向けてまっすぐ撮影します。カメラをテクスチャにレンダリングし、そのパネルのみをカメラにレンダリングして、カメラを破棄します。

次に、テクスチャを png にエンコードします

  1. これは恐ろしく高価になる可能性があります。
  2. カメラのサイズをパネルに合わせない限り、画面の残りの部分もレンダリングされます。パネルが正方形または長方形でない場合でも、ショットでゲーム/スカイボックス/背景色の一部が取得されます。
  3. それは単に機能しない可能性があります
于 2016-04-11T17:59:20.110 に答える