2016-04-11 6 views
5

Unity.UI Canvasについて考えてみましょう。Unity3D、Unity.UIのパネルからPNGをビルドしますか?

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

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

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

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

enter image description here

enter image description hereは、ピンクのパネルPNG画像を作ります。ああ。

(誰でももちろん、むしろ1枚のパネルよりも、単に「全体キャンバス」を行い解決策を持っている場合はもちろん、でもそれは立派である。)これはこれでちょうど速い刺しは、私ができることから

+0

これは実際には複雑に聞こえます。スクリーンショットを撮ることは一つのことですが、パネルのスクリーンショットを撮ることは別の話です。パネルのあなたの言うスクリーンショットは、パネルの子であるUIを意味しますか? – Programmer

+0

**すべての** **パネル内のUIを意味しますか? – Programmer

+0

右 - メニューまたはコンテキストメニュー "create - > UI"を使用してパネルを作成すると、パネル、つまり 'CanvasRenderer'、つまり 'パネル'として知られています。 – Fattie

答えて

4

以下のコードは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 
} 
+0

あなたがテストを終えたら、問題があれば教えてください – Programmer

+1

これはうまくいくようです。私は他の何かが求められるとは思わない!素晴らしいもの! – Fattie

+0

喜んで私は手伝ってくれました。古いコードを戻すことについてのあなたのコメントについて言えば、それはなくなっています。私はそれを選択し、それらのすべてを削除し、パフォーマンスが要件であると思った後、上記のコードでやり直した。私はビジュアルスタジオを閉じて再オープンしたので、それ。 Un-doがなくなった..... – Programmer

1

は」それを行うための公式の簡単な方法を見つける。

私はそれを試していないので、これはどのようにパフォーマンスが集中するかわかりません。

UIパネルをポイントしてカメラを追加すると、まっすぐに撮影できます。カメラにレンダリングされているパネルだけを使用して、カメラをテクスチャにレンダリングし、カメラを破棄します。

Then encode the texture to a png

  1. これはひどく高価です。
  2. カメラのサイズをパネルに合わせないと、残りの画面もレンダリングされます。パネルが正方形または の長方形でない場合でも、 のゲーム/スカイボックス/背景色の一部になります。ショット。
  3. それは単なる
1

私は深くそれに見ていないと動作しませんでしたが、私は親のキャンバスをレンダリングせずにパネルをレンダリングすることは不可能であると仮定します。私はあなたがこれを毎フレーム行うのではなく、特定の機会にのみ行うことを望んでいると仮定しています。その文脈では

、ここで私がしようとするものです。

  • はRenderMode「スクリーンスペース - カメラ」を有する第二のキャンバスを持っています。これにより、このキャンバスのレンダリングに使用するカメラを指定できます。
  • 2台目のキャンバスをレンダリングする専用のカメラを用意してください。
  • カメラにOnPreCullとOnPostRenderを処理するスクリプトを与えます。
  • OnPreCull、セカンダリCanvasにターゲットパネルを接続します。 OnPostRenderは、以前の場所に再接続します(私はそうです...とても残念)
  • はもちろん

サイズと位置と何のような不特定の詳細の多くは、そこにあるほら、EncodeToPNGを適用し、第二カメラは、RenderTextureに

  • ReadPixelsをレンダリングしてみましょう。しかしCanvasとCameraを専用にしておけば、これをすべて把握してセットアップを正しく行うことができます。

  • +0

    "しかし、特定の機会にのみ。"、確かに - 1つだけオフとして。私はあなたの方法を勉強します! – Fattie

    +0

    ああ、OnPreRenderがすでに遅すぎてPanelが実際にレンダリングに含まれていない可能性があります。 OnPreCullを提案する答えを変更します。 –

    +0

    私は、重要な洞察は、本質的に、問題のパネルを複製した別のカメラが必要であるかもしれないと思う(結局、編成が難しくない)。おそらくそれがKISSソリューションです。 – Fattie

    関連する問題