Unity3D,从Unity.UI面板构建PNG吗? [英] Unity3D, build PNG from Panel of a Unity.UI?
问题描述
考虑一下您可能拥有的任何Unity.UI Canvas
.
Think about any Unity.UI Canvas
you might have.
想象一下该画布上的典型Panel
.假设它恰好包含一些图像,也许包含一些文本,等等.
Imagine a typical Panel
on that Canvas. Say it happens to contain some images, maybe some Text and so on.
如果您可以将该面板(仅该面板)翻成屏幕快照:Texture2D或PNG,将非常方便.
It would be very handy if you could turn that panel (only the panel) in to a screenshot: a Texture2D or PNG.
我唯一想到的就是使用ReadPixels
并找出屏幕上有问题的Panel
的区域(实际上这很棘手);并且只有在面板恰好是正方形且没有旋转角度时才起作用.
The only thing I can think of is just using ReadPixels
and figuring out the area of the Panel
in question on the screen (and actually that is pretty tricky); and that only works if the panel happens to be square and not rotated at an angle.
您会认为,应该有一种方法可以渲染一个面板,或者至少渲染整个画布?我什么也找不到.
You'd think, there should be a way to render one Panel, or at least, one whole canvas? I can find nothing.
在示例中,将粉红色的Panel设为PNG图像.哎呀.
In the example, make the pink Panel a PNG image. Ouch.
(很明显,如果有人有一个解决方案只执行整个画布",而不是一个面板,那当然是令人钦佩的.)
(Obviously, if anyone has a solution that does merely "the whole Canvas", rather than one Panel, of course even that is admirable.)
推荐答案
下面的代码可以拍摄Canvas的图片.必须将画布附加到您要传递到其中的对象.唯一要调用的功能是void takeScreenShot(Canvas canvasPanel, SCREENSHOT_TYPE screenShotType = SCREENSHOT_TYPE.IMAGE_AND_TEXT, bool createNewInstance = true)
The code below can take picture of a Canvas. Canvas must be attached the Object you are passing into it. The only function to call is void takeScreenShot(Canvas canvasPanel, SCREENSHOT_TYPE screenShotType = SCREENSHOT_TYPE.IMAGE_AND_TEXT, bool createNewInstance = true)
SCREENSHOT_TYPE.IMAGE_AND_TEXT
参数将拍摄图像和文本的图片.
SCREENSHOT_TYPE.IMAGE_AND_TEXT
parameter will take picture of images and texts.
SCREENSHOT_TYPE.IMAGE_ONLY
参数将仅拍摄图像的图片.屏幕上的所有文本都将被排除.出于安全原因,您可以使用它删除文本并仅显示图形.
SCREENSHOT_TYPE.IMAGE_ONLY
parameter will only take picture of images. Every texts on the screen will be excluded. You can use this for security reason to remove texts and just show graphics only.
SCREENSHOT_TYPE.TEXT_ONLY
参数仅拍摄文本的图片.
使用方法.创建一个GameObject,将CanvasScreenShot脚本附加到它.订阅CanvasScreenShot.OnPictureTaken(byte[] pngArray)
;,然后致电screenShot.takeScreenShot(canvasToSreenShot, SCREENSHOT_TYPE.IMAGE_AND_TEXT, false);
How to use. Create a GameObject, attach the CanvasScreenShot script to it. Subscribe to CanvasScreenShot.OnPictureTaken(byte[] pngArray)
;, then call 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
}
这篇关于Unity3D,从Unity.UI面板构建PNG吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!