2016-04-11 11 views
5

Denken Sie über irgendeine Unity.UI Canvas, die Sie haben könnten.Unity3D, PNG vom Panel einer Unity.UI erstellen?

Stellen Sie sich eine typische Panel auf diesem Canvas. Angenommen, es enthält einige Bilder, vielleicht etwas Text und so weiter.

Es wäre sehr praktisch, wenn Sie dieses Panel (nur das Panel) in einen Screenshot verwandeln könnten: ein Texture2D oder PNG.

Das einzige, was ich denken kann, ist nur mit ReadPixels und herauszufinden, den Bereich der Panel in Frage auf dem Bildschirm (und eigentlich ist das ziemlich schwierig); und das funktioniert nur, wenn das Panel quadratisch und nicht in einem Winkel gedreht ist.

Sie denken, es sollte eine Möglichkeit geben, ein Panel oder zumindest eine ganze Leinwand zu rendern? Ich kann nichts finden.

enter image description here

enter image description here

Im Beispiel machen die rosa-Panel ein PNG-Bild. Autsch.

(Natürlich, wenn jemand eine Lösung hat, die lediglich bedeutet „die ganze Leinwand“, eher als ein Gremium, natürlich auch das ist bewundernswert.)

+0

Das eigentlich klingt kompliziert. Screenshot zu machen ist eine Sache, aber Screenshot eines Panels zu machen ist eine andere Geschichte. Wenn Sie sagen, Bildschirmfoto eines Panels meinen Sie UI, die ein Kind des Panels ist? – Programmer

+0

Meinst du ** alle ** UI im Panel? – Programmer

+0

Rechts - ein Panel, also ein 'CanvasRenderer', umgangssprachlich als 'Panel' bekannt, wenn man es über das Menü oder Kontextmenü" create -> UI "erstellt .... image hinzugefügt! – Fattie

Antwort

4

Der folgende Code kann ein Canvas-Bild aufnehmen. Canvas muss an das Objekt angehängt werden, an das Sie es übergeben. Die einzige Funktion aufzurufen ist void takeScreenShot(Canvas canvasPanel, SCREENSHOT_TYPE screenShotType = SCREENSHOT_TYPE.IMAGE_AND_TEXT, bool createNewInstance = true)

SCREENSHOT_TYPE.IMAGE_AND_TEXT Parameter Bild von Bilder und Texte statt.

SCREENSHOT_TYPE.IMAGE_ONLY Parameter wird nur Bild von Bilder. Alle Texte auf dem Bildschirm werden ausgeschlossen. Sie können dies aus Sicherheitsgründen verwenden, um Texte zu entfernen und nur Grafiken anzuzeigen.

SCREENSHOT_TYPE.TEXT_ONLY Parameter wird Bild von Texte nur nehmen.

Wie zu verwenden. Erstellen Sie ein GameObject und hängen Sie das CanvasScreenShot-Skript daran an. Melden Sie sich an CanvasScreenShot.OnPictureTaken(byte[] pngArray), dann rufen screenShot.takeScreenShot(canvasToSreenShot, SCREENSHOT_TYPE.IMAGE_AND_TEXT, false);

kompletten Code:

Ihre test.cs Skript:

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); 
    } 

} 

Das CanvasScreenShot.cs Skript:

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

Wenn Sie mit dem Testen fertig sind, lassen Sie mich, wenn es ein Problem gibt – Programmer

+1

das scheint gut zu funktionieren. Ich glaube nicht, dass noch etwas verlangt werden kann! tolles Zeug! – Fattie

+0

Ich bin froh, dass ich helfen konnte. Wenn Sie über Ihren Kommentar zum Zurückbringen des alten Codes sprechen, ist er weg. Ich wählte es aus und löschte alle von ihnen und begann wieder mit dem obigen Code, nachdem ich dachte, dass Leistung eine Voraussetzung ist. Ich glaube nicht, dass ich es jemals zurückbekommen werde, weil ich visuelles Studio geschlossen habe und wieder geöffnet habe es. Un-Do ist weg ..... – Programmer

1

Dies ist nur ein kurzer Stich an mich, da ich kann‘ Ich finde keinen offiziellen einfachen Weg, es zu tun.

Ich habe keine Ahnung, wie leistungsintensiv dies wäre, da ich es nicht ausprobiert habe.

Fügen Sie eine Kamera hinzu, die auf das UI-Bedienfeld zeigt, um eine direkte Aufnahme zu erhalten. Rendern Sie die Kamera auf eine Textur, wobei nur das Panel auf der Kamera gerendert wird, zerstören Sie die Kamera.

Then encode the texture to a png.

  1. Das könnte furchtbar teuer sein.
  2. Es wäre auch den Rest des Bildschirms machen, wenn Sie die Kamera an die Platte, und selbst dann, wenn die Panel-Größe nicht ein Quadrat oder Rechteck ist, werden Sie Teil der Spiel/Skybox/Hintergrundfarbe in erhalten der Schuss.
  3. Es könnte nur funktionieren schlicht nicht
1

Ich habe tief in sie nicht sah, aber ich nehme an, es unmöglich ist, ein Gremium zu machen, ohne seine Eltern Leinwand macht. Ich gehe auch davon aus, dass Sie das nicht in jedem Frame machen wollen, sondern nur zu bestimmten Anlässen.

In diesem Zusammenhang ist hier, was ich versuchen würde:

  • eine zweite Leinwand mit Render "Screen Space - Kamera" haben. Damit können Sie die Kamera angeben, die zum Rendern dieses Canvas verwendet wird.
  • Haben Sie eine dedizierte Kamera, um Ihre zweite Leinwand zu rendern.
  • Geben Sie der Kamera ein Skript, das OnPreCull und OnPostRender behandelt
  • OnPreCull, hängen Sie das Ziel-Panel an Ihre sekundäre Canvas an. OnPostRender, setze es wieder dort an, wo es vorher war (ich bin so ...so sorry)
  • die zweite Kamera auf einen RenderTexture
  • ReadPixels machen lassen, Anwenden, EncodeToPNG, voila

Natürlich gibt es viele Details nicht spezifiziert, wie Größen und Positionen und was auch immer. Aber nachdem Canvas und Camera dediziert wurden, sollte es möglich sein, das alles richtig zu erkennen und zu konfigurieren, ohne etwas kaputt zu machen.

+0

"aber nur zu bestimmten Anlässen.", Sicher schon - nur als Einzelstück. Ich werde deine Methode studieren! – Fattie

+0

Oh, es könnte sein, dass OnPreRender bereits zu spät ist, um das Panel tatsächlich in das Rendering zu integrieren. Ich werde meine Antwort ändern, um OnPreCull vorzuschlagen. –

+0

Ich denke, die wichtigste Erkenntnis könnte sein, dass Sie im Wesentlichen eine andere Kamera mit einem Duplikat des fraglichen Panels brauchen (was schließlich nicht so schwer zu organisieren ist). vielleicht ist das die KISS-Lösung. – Fattie

Verwandte Themen