Read/Load image file from the StreamingAssets folder - c#

I have a png image inside of my StreamingAssets folder. When I try to retrieve the image, it returns an image with a red question mark (this is not the right image).
I am using this script:
public string url;
IEnumerator Start()
{
url = Application.dataPath + "/StreamingAssets/shareImage.png";
using (WWW www = new WWW(url))
{
yield return www;
Renderer renderer = GetComponent<Renderer>();
renderer.material.mainTexture = www.texture;
}
}
What should I do the get the right image?
Location of the image:
The Inspector tab of the image I want to change:

While reading files in the StreamingAssets folder, you have to use WWW on some platforms or File.ReadAllBytes some others. You check if the path contains :// or :/// then determine which one to use. This should solve the question mark issue. Note that you should now be using UnityWebRequest where WWW is required unless there is a bug with UnityWebRequest.
Another issue that will arise is the SpriteRenderer image not being upadte. The material will reflect the images changes but the SpriteRenderer will not. The proper way to change a sprite of a SpriteRenderer is to change the SpriteRenderer.sprite property. This means that you will have to Convert the Texture2D to Sprite then assign that sprite to the SpriteRenderer.sprite property.
Finally, use Application.streamingAssetsPath when accessing files in the StreamingAssets folder. This is not the issue now since you're running it in the Editor but will be in a build.
Below is what your code for reading image file in the StreamingAssets should look like:
using UnityEngine.Networking;
public string url;
IEnumerator Start()
{
///url = Application.dataPath + "/StreamingAssets/shareImage.png";
url = Path.Combine(Application.streamingAssetsPath, "shareImage.png");
byte[] imgData;
Texture2D tex = new Texture2D(2, 2);
//Check if we should use UnityWebRequest or File.ReadAllBytes
if (url.Contains("://") || url.Contains(":///"))
{
UnityWebRequest www = UnityWebRequest.Get(url);
yield return www.SendWebRequest();
imgData = www.downloadHandler.data;
}
else
{
imgData = File.ReadAllBytes(url);
}
Debug.Log(imgData.Length);
//Load raw Data into Texture2D
tex.LoadImage(imgData);
//Convert Texture2D to Sprite
Vector2 pivot = new Vector2(0.5f, 0.5f);
Sprite sprite = Sprite.Create(tex, new Rect(0.0f, 0.0f, tex.width, tex.height), pivot, 100.0f);
//Apply Sprite to SpriteRenderer
SpriteRenderer renderer = GetComponent<SpriteRenderer>();
renderer.sprite = sprite;
}

Related

Make Material From Texture2D

I'm trying to write a program in unity. I'm quite new to unity, so i don't know much.i want to be able to make a new material in my code with only a texture2d to pass into it. So far im trying this:
using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEngine;
using UnityEngine.UI;
public class Search : MonoBehaviour
{
public void SearchEnter()
{
MyObject.GetComponent<Image>().material = LoadImage(name.ToLower());
}
Texture2D LoadImage(string ImageName)
{
string url = "file:///C:/MyFilePath" + ImageName + ".jpg";
Texture2D tex;
tex = new Texture2D(4, 4, TextureFormat.DXT1, false);
using (WWW www = new WWW(url))
{
www.LoadImageIntoTexture(tex);
GetComponent<Renderer>().material.mainTexture = tex;
}
return tex;
}
}
I cannot do this as it cannot convert from texture2d to material.
Can anyone help me?
First of all you are not waiting until the WWW request finishes so you are trying to access the result to early.
Instead of the WWW you should rather use a UnityWebRequestTexture.GetTexture in a Coroutine like
public class Search : MonoBehaviour
{
public void SearchEnter()
{
StartCoroutine(LoadImage(name.ToLower()))
}
IEnumerator LoadImage(string ImageName)
{
using (UnityWebRequest uwr = UnityWebRequestTexture.GetTexture("file:///C:/MyFilePath" + ImageName + ".jpg"))
{
yield return uwr.SendWebRequest();
if (uwr.isNetworkError || uwr.isHttpError)
{
Debug.Log(uwr.error);
}
else
{
// Get downloaded texture
var texture = DownloadHandlerTexture.GetContent(uwr);
//TODO now use it
...
}
}
}
}
The second issue: You can not simply convert a Texture2D to Material. You rather have to either create a new Material from a given shader or a given Material using new Material() e.g. using Shader.Find like
//TODO now use it
// ofcourse again you can not simply create a new material from a texture
// you would rather need to pass in a shader like
var material = new Material(Shader.Find("UI/Default"));
material.mainTexture = texture;
MyObject.GetComponent<Image>().material = material;
GetComponent<Renderer>().material.mainTexture = texture;
However, for an Image you would actually probably rather simply stick with the default UI material but use a Sprite instead
MyObject.GetComponent<Image>().sprite = Sprite.Create(texture, new Rect(0, 0, texture.width, texture.height), Vector2.one * 0.5f);

Using Unity Editor, how do i upload a file from my computer and have it appear on a 3D object or plane?

I found a tutorial on YouTube that accurately added File Explorer and image upload to a 'RawImage' on a canvas using Unity 2017.3.1f1.
What I'm trying to do is add the same image after 'button press' to a 3D object like a cube or plane as shown by the colored cube. When I run the below code, it registers as being present on the cube but doesn't render. Any help is appreciated.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEditor;
public class Explorer : MonoBehaviour
{
string path;
public RawImage image;
public void OpenExplorer()
{
path = EditorUtility.OpenFilePanel("Overwrite with png", "", "png");
GetImage();
}
void GetImage()
{
if (path != null)
{
UpdateImage();
}
}
void UpdateImage()
{
WWW www = new WWW("file:///" + path);
image.texture = www.texture;
}
}
There is a tiny bug in your code. It should work sometimes and fail other times. The chances of it working or not depends on the size of the image. It will work if the image is really small but fail when it is a large image.
The reason for this is because of the code in your UpdateImage function. The WWW is supposed to be used in a coroutine function because you need to yield or wait for it to finish loading or downloading the file before accessing the texture with www.texture. Your are not doing this now. Change it to a coroutine function then yield it and it should work fine,.
void GetImage()
{
if (path != null)
{
StartCoroutine(UpdateImage());
}
}
IEnumerator UpdateImage()
{
WWW www = new WWW("file:///" + path);
yield return www;
image.texture = www.texture;
}
If some reason you can't use a coroutine because it's an Editor plugin then forget about the WWW API and use use File.ReadAllBytes to read the image.
void GetImage()
{
if (path != null)
{
UpdateImage();
}
}
void UpdateImage()
{
byte[] imgByte = File.ReadAllBytes(path);
Texture2D texture = new Texture2D(2, 2);
texture.LoadImage(imgByte);
image.texture = texture;
}
To assign the image to a 3D Object, get the MeshRenderer then set the texture to the mainTexture of the material the renderer is using:
//Drag the 3D Object here
public MeshRenderer mRenderer;
void UpdateImage()
{
byte[] imgByte = File.ReadAllBytes(path);
Texture2D texture = new Texture2D(2, 2);
texture.LoadImage(imgByte);
mRenderer.material.mainTexture = texture;
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEditor;
using System.IO;
public class Explorer : MonoBehaviour
{
string path;
public MeshRenderer mRenderer;
public void OpenExplorer()
{
path = EditorUtility.OpenFilePanel("Overwrite with png", "", "png");
GetImage();
}
void GetImage()
{
if (path != null)
{
UpdateImage();
}
}
void UpdateImage()
{
byte[] imgByte = File.ReadAllBytes(path);
Texture2D texture = new Texture2D (2, 2);
texture.LoadImage(imgByte);
mRenderer.material.mainTexture = texture;
//WWW www = new WWW("file:///" + path);
//yield return www;
//image.texture = texture;
}
}

Adding clickable gameobject c#

So I'm dynamically adding images to a scrollview from a json file/ url
I like to make the images clickable gameobject, but have no idea how to do this in c# ( void OnMouseDown() ???)
IEnumerator AddFeedItem (int index)
{
WWW www = new WWW (ModelURL); // img url
yield return www;
GameObject newsimageObject = tempFeedItem.transform.FindChild ("newsimage").gameObject;
Image newsImage = newsimageObject.GetComponent<Image> ();
Texture2D tempTex = photos [index % 10];
SpriteRenderer renderer = newsImage.GetComponent<SpriteRenderer>();
Sprite sprite = new Sprite();
sprite = Sprite.Create(www.texture, new Rect(0, 0, 455, 230),new Vector2(0, 0),100.0f);
newsImage.sprite = sprite;
}
This is an Image/Canvas. You should avoid Raycast and OnMouseDown. To detect touch with Image/Canvas, you use have to derive from IPointerDownHandler or IPointerClickHandler then implement the functions from them. OnMouseDown or Raycast should be used only if the Object is a 3D model or if the Object is a Sprite that is NOT under a Canvas. In that case, a collider is required. In this case, you are using the Image component, so I assume this is under Canvas.
public class YourClass : MonoBehaviour,IPointerDownHandler,IPointerClickHandler
{
public void OnPointerClick(PointerEventData eventData)
{
Debug.Log("Clicked");
}
public void OnPointerDown(PointerEventData eventData)
{
Debug.Log("Down");
}
}
Try to create a BoxCollider2D object atached to that GameObject and then Use
Physics2D.Raycast(Camera.main.ScreenToWorldPoint(Mouse.position), Vector2.zero);
to test if there are any Collider in the Mouse.position.

Load sprites from persistent data path

I am downloading some sprites from my server and store them in Application.persistentDataPath.
However, I cannot load the controller using Resources.Load (controllerPath) because the path is outside the Resources folder.
Additionally, I get a MissingComponentException when I try to add the animation controller to the GameObject.
Here is my code:
private GameObject SideSprite;
// ...
string controllerPath = Application.persistentDataPath+"/"+aux+"/"+aux+"Controller";
controller = (RuntimeAnimatorController)Resources.Load (controllerPath); // Returns null
// Below I get:
// MissingComponentException: There is no 'Animator' attached to the
// "Missing Prefab (Dummy)" game object, but a script is trying to access it.
SideSprite.GetComponent<Animator> ().runtimeAnimatorController = controller;
How should I load the resources from the persistent data path?
persistentDataPath is used as any regular folder. I would not store a Sprite but more likely a texture and next time you need it you unroll the process of applying a texture to a sprite:
public static void StoreCacheSprite(string url, Sprite sprite)
{
if(sprite == null || string.IsNullOrEmpty(url) == true) { return; }
SpriteRenderer spRend = sprite.GetComponent<SpriteRenderer>();
Texture2D tex = spRend.material.mainTexture;
byte[] bytes = tex.EncodeToPNG();
string path = Path.Combine(Application.persistentDataPath, url);
File.WriteAllBytes(Application.persistentDataPath, bytes);
}
public static Sprite GetCacheSprite(string url)
{
if( string.IsNullOrEmpty(url) == true) { return; }
string path = Path.Combine(Application.persistentDataPath, url);
if(File.Exists(path) == true)
{
bytes = File.ReadAllBytes(path);
Texture2D texture = new Texture2D(4, 4, TextureFormat.RGBA32, false);
texture.LoadImage(bytes);
Sprite sp = Sprite.Create(texture, new Rect(0,0 texture.width, texture.height, new Vector2(0.5f,0.5f));
return sp;
}
return null;
}
The first method stores the texture using the File class from .NET. It converts and writes a byte array onto the ROM of the device (File.WriteAllBytes). You need a path to a Sprite and a name for it. That name needs to comply with file and folder path naming.
The second method does the inverse process, checking if it is already stored and turning the byte array found on the RAM into a usable Sprite.
You could also simply use WWW to get the data.
string controllerPath = Application.persistentDataPath+"/"+aux+"/"+aux+"Controller";
IEnumerator Start ()
{
WWW www = new WWW("file:///" + controllerPath);
yield return www;
Debug.Log(www.texture); //or www.bytes
}

download png before loading scene

I develop Web Player application.
I need to download *.png image and use this image in scene.
Download code:
public Material mat;
string fullFilename;
Texture2D texTmp;
Sprite spr;
void Awake()
{
fullFilename = "http://585649.workwork.web.hostingtest.net/Images/Logo.png";
StartCoroutine(Download());
texTmp = new Texture2D(50, 50);
spr = Sprite.Create(texTmp, new Rect(0, 0, texTmp.width, texTmp.height), Vector2.zero, 100);
spr.texture.wrapMode = TextureWrapMode.Clamp;
mat.mainTexture = spr.texture;
}
IEnumerator Download()
{
WWW www = new WWW(fullFilename);
yield return www;
www.LoadImageIntoTexture(texTmp);
}
This work fine,but after loading scene uploaded picture appears after a while.
How i can fix it ?
Sorry for my English :)
Thanks!
This is natural. Because you download picture from internet, and there are some delays. So you add loading screen or wait all scene until picture is downloaded by you. But i think it is not good solution because you only load picture. Maybe disabling other buttons/interactive elements before starting download and then enable them after download is finished is good solution.
For example:
void Awake()
{
fullFilename = "http://585649.workwork.web.hostingtest.net/Images/Logo.png";
disableButtons();
StartCoroutine(Download());
texTmp = new Texture2D(50, 50);
spr = Sprite.Create(texTmp, new Rect(0, 0, texTmp.width, texTmp.height), Vector2.zero, 100);
spr.texture.wrapMode = TextureWrapMode.Clamp;
mat.mainTexture = spr.texture;
}
IEnumerator Download()
{
WWW www = new WWW(fullFilename);
yield return www;
www.LoadImageIntoTexture(texTmp);
enableButtons();
}

Categories

Resources