- Home /
Trying to Apply a screenshot to a raw image, only works on second try...
I’m at the point in my (tabletop simulator) project where I’m doing the team setup system. The player enters a name, type, other unit info in the fields on the unit setup screen. They choose a model by toggling between different units in the scene, and a secondary camera shows the unit in a raw image on that screen. They choose the material they want the unit to have, click create and then this CreateUnitCard() function is called. This will instantiate a new UI prefab which will show the name, type, other info and a screenshot of that unit.I watched a Code Monkey video to learn how to take a screenshot, I have to admit I don’t 100% understand it.
The problem is not in the actual screenshot taking, I don’t think – It pops up in the resources folder and looks fine to me. I have noticed that I need to click off of unity and reselect the program before it becomes visible, but it is there. The screenshot just doesn’t get applied to the raw image on the unit card. That remains white. Texture is: None (Texture).
The Screenshot function saves an image and names it after the unit. I have noticed that if I create a unit with a name that matches an image already saved to the resources folder, it does work. So If I try to make “Bob” his screenshot will be saved to the resources folder, but it will not display the screenshot on his card. If I then exit play mode, get back in and try to make “Bob” again, it will work as intended. If anyone could shine some light on this issue I would greatly appreciate it!
Here's the CreateUnitCard function-
public void CreateUnitCard()
{
GameObject newCard = Instantiate(unitCard, TeamPanel.transform.position, transform.rotation);
newCard.transform.SetParent(TeamPanel.transform);
Text nameLine = newCard.gameObject.transform.Find("NameText").GetComponent<Text>();
nameLine.text = unitName;
Text typeLine = newCard.gameObject.transform.Find("TypeText").GetComponent<Text>();
typeLine.text = unitType;
Text otherInfoLine = newCard.gameObject.transform.Find("OtherInfoText").GetComponent<Text>();
otherInfoLine.text = otherUnitInfo;
ScreenshotHandler.TakeScreenshot(200, 300, unitName);
Texture2D UnitPhoto = Resources.Load<Texture2D>(unitName);
RawImage unitImageHolder = newCard.gameObject.transform.Find("Thumbnail").GetComponent<RawImage>();
unitImageHolder.texture = UnitPhoto;
//Reset text variables
}
AND this is the Code Monkey script I'm using -
public class ScreenshotHandler : MonoBehaviour
{
private static ScreenshotHandler instance;
[SerializeField]
Camera myCamera;
private bool takeScreenshotOnNextFrame;
static string unitName;
private void Awake()
{
instance = this;
}
private void OnPostRender()
{
if (takeScreenshotOnNextFrame)
{
takeScreenshotOnNextFrame = false;
RenderTexture renderTexture = myCamera.targetTexture;
Texture2D renderResult = new Texture2D(renderTexture.width, renderTexture.height, TextureFormat.RGB24, false);
Rect rect = new Rect(0, 0, renderTexture.width, renderTexture.height);
renderResult.ReadPixels(rect, 0, 0);
byte[] byteArray = renderResult.EncodeToPNG();
System.IO.File.WriteAllBytes(Application.dataPath + "/Resources/" + unitName + ".png", byteArray);
Debug.Log("Saved Screenshot " + unitName + ".png");
RenderTexture.ReleaseTemporary(renderTexture);
myCamera.targetTexture = null;
}
}
private void TakeScreenshot(int width, int height)
{
myCamera.targetTexture = RenderTexture.GetTemporary(width, height, 16);
takeScreenshotOnNextFrame = true;
}
public static void TakeScreenshot(int width, int height, string name)
{
unitName = name;
instance.TakeScreenshot(width, height);
}
}
This is the create a unit menu -
And here's the main unity window, showing that the unit card is there, the create unit function has run, the image has saved but it was not applied to the Raw Image on the card-
If you have read this far, you are awesome. Thanks for any help!
Answer by Vivien_Lynn · Mar 26, 2021 at 04:31 PM
It looks like you try to assign your texture (Screenshot) before the screenshot got taken and saved. If you look at the code in your ScreenshotHandler
, you see that the Screenshot gets taken in the next frame. That means that the Texture does not yet exist when you try to assign it. One simple fix should be the following: Put the code that assigns the texture to your card into a Coroutine, like so:
private IEnumerator AssignTextureToCard()
{
yield return 0;
yield return new WaitForEndOfFrame();
Texture2D UnitPhoto = Resources.Load<Texture2D>(unitName);
RawImage unitImageHolder = newCard.gameObject.transform.Find("Thumbnail").GetComponent<RawImage>();
unitImageHolder.texture = UnitPhoto;
//Reset text variables
}
Call this Coroutine after Line 16 in your first script, like so: StartCoroutine(AssignTextureToCard());
This Coroutine waits one frame (
yield return 0;
)In the next frame, it waits for the frame to end (
yield return new WaitForEndOfFrame();
)Then it assignes the Texture to your card
This is necessary because of Unity's Execution Order (click here for more information). We do yield return 0
to make sure we are in the same frame as when the Screenshot gets taken. With yield return new WaitForEndOfFrame();
we make sure that the Screenshot is actually taken and saved, before we try to access it (See in the Execution Order how the Screen gets rendered, before yield WaitForEndOfFrame
)
@Vivien_Lynn Thank you so much for answering, I feel like I've won the lottery! And such a thoughtful answer too! I really appreciate the time you've taken.
But sadly I have to report that this solution did not fix the situation. I'd say it has improved, since the image in the Thumbnail now says "Missing(Texture)" instead of "None(Texture)". Here's how it looks now (The changed parts):
ScreenshotHandler.TakeScreenshot(200, 300, unitName);
StartCoroutine(AssignTextureToCard(newCard));
}
private IEnumerator AssignTextureToCard(GameObject newCard)
{
yield return 0;
yield return new WaitForEndOfFrame();
Texture2D UnitPhoto = Resources.Load<Texture2D>(unitName);
RawImage unitImageHolder =
newCard.gameObject.transform.Find("Thumbnail").GetComponent<RawImage>();
unitImageHolder.texture = UnitPhoto;
}
I had to do the newCard as a parameter of AssignTextureToCard because it's a local variable I guess.
I had another idea and put a similar script on the actual unit card, so right when it's instantiated it uses the text in the name field to load a .png with the same name from resources after .1 seconds, but the result was the same. Missing texture. I would've thought that if I tried to load an image that didn't exist yet, I'd get an error of some kind, but there is none! Anyway, thanks again for your help, I never expected such a thorough answer. If you have any other ideas I'd love to hear them. If not, no problem!
It is correct that you need a parameter, to have a reference to your newCard. Can you confirm that the code still works when you execute it the second time? If so, you could try to add a second yield return 0;
right after the first one. You need to see with Debug messages if your code gets executed correctly, and if all the references work.
@Vivien_Lynn It still does work when I create another unit with the same name. I tested something else - looking in the file explorer resources folder, I can see the screenshot pop up immediately when the screenshot is taken, but there is no metadata for it right away. It's probably just a coincidence. But the metadata pops up in there when I exit play, click off of unity and click back on it. That's also when the screenshot appears in the resources folder in unity. Could it be a weird project setting or something? It looks like everything's running, it just.... Won't... Load!!!! I'm stumped!
I am afraid I have no answer to that particular phenomenon. Maybe you should ask about this specifically; "How to apply screenshot to a raw image in runtime?". Keep it short, but give all the information needed to understand the problem, you wrote a bit much in your original post. This increases the chance that people will actually read and understand the question. That being said, I personally would have done this differently. Instead of saving a screenshot (write file) and then loading it (read file), I would have just taken the Render Texture that gets generated in the ScreenshotHandler
, and apply that to the Raw Image.
@Vivien_Lynn Well thank you very much for trying to help! This issue has been holding me up for almost a week now, I think I'm just going to skip it (it is a cosmetic issue after all, not really my current priority as you can see from the screenshots) and come back to it later. I still have a long way to go. But when I do come back to it I'll try what you suggested. Have a good day!
I just remembered that $$anonymous$$ander Zotov once made a tutorial about showing a screenshot in Unity, during runtime. Maybe that is helpful for you. Here is the link: https://youtu.be/DQeylS0l4S4 Good luck! :)
Your answer
Follow this Question
Related Questions
How to do screenshot and save it in phone as png(not texture)? 0 Answers
How to play Video on canvas (RawImage)? 0 Answers
Using Texture2D.GetPixels() to take a screenshot and then show it on an Image - iOS problems. 1 Answer
Share image in Unity Game on Android devices error 2 Answers
How do i set screenshot image of Scene2 to Scene1(Button) in unity. Thanks in adavance 1 Answer