Right now I am developing a VR-application for my Oculus Go. In this Application i need to import files like images, video, audio during runtime. My Problem right now is that the import of these files do work in unity, but doesn't work on my oculus go.
In my project i use a file browser prefab from the asset store. this file browser can be opened during the runtime of my application, but when i want to open a file for example an image, it only turns grey. the image should be loaded to a RawImage-Object.
Filebrowser during runtime
RawImage-Object turns grey
Unity-does work
I dont' understand why this is happening. I have to say I'm new to unity and would appreciate any kind of help! Here is the code of the image-load-script.
public class FileManager : MonoBehaviour {
public RawImage image;
public void OpenExplorer()
{
SimpleFileBrowser.FileBrowser.SetFilters(true, ".txt", ".jpg", ".png", ".mp4");
SimpleFileBrowser.FileBrowser.ShowLoadDialog( (path) => { UpdateImage(path); }, null, false, null, "Select File", "Select");
}
void UpdateImage(string pfad)
{
if (pfad != null)
{
WWW www = new WWW(pfad);
image.texture = www.texture;
var texWidth = www.texture.width;
var texHeight = www.texture.height;
if (texWidth > texHeight)
{
GetComponent<RectTransform>().sizeDelta = new Vector2(1920/2, 1080/2);
}
if (texWidth < texHeight)
{
GetComponent<RectTransform>().sizeDelta = new Vector2(1080/2, 1920/2);
}
}
}
My Problem right now is that the import of these files do work in
unity, but doesn't work on my oculus go.
Most buggy code work on the Editor but you will discover the issue with a code when you deploy or build the project.
Your problem is in the UpdateImage function:
WWW www = new WWW(pfad);
image.texture = www.texture;
...
You are supposed to yield or wait for the WWW request to finish before loading the image. The request asynchronous. Without yielding it, you would be trying to accessing incomplete image leading to a corrupted image or gray image depending on the platform.
Make the following changes:
1.Change the UpdateImage to a coroutine function (void to IEnumerator) then yield the WWW request with yield return www; before accessing the texture from it:
2.Call the UpdateImage function as a coroutine function with StartCoroutine:
Replace:
SimpleFileBrowser.FileBrowser.ShowLoadDialog( (path) => { UpdateImage(path); }, null, false, null, "Select File", "Select");
with
SimpleFileBrowser.FileBrowser.ShowLoadDialog( (path) => { StartCoroutine(UpdateImage(path)); }, null, false, null, "Select File", "Select");
3.Check for error before accessing the Texture. When the WWW request has finished, there could be an error. Check that before using the returned texture or item.
The final code should look like this:
public RawImage image;
public void OpenExplorer()
{
SimpleFileBrowser.FileBrowser.SetFilters(true, ".txt", ".jpg", ".png", ".mp4");
SimpleFileBrowser.FileBrowser.ShowLoadDialog((path) => { StartCoroutine(UpdateImage(path)); }, null, false, null, "Select File", "Select");
}
IEnumerator UpdateImage(string pfad)
{
if (pfad != null)
{
WWW www = new WWW(pfad);
//WAIT UNTIL REQUEST IS DONE!
yield return www;
//Check for error
if (!string.IsNullOrEmpty(www.error))
{
Debug.Log(www.error);
}
else
{
image.texture = www.texture;
var texWidth = www.texture.width;
var texHeight = www.texture.height;
if (texWidth > texHeight)
{
GetComponent<RectTransform>().sizeDelta = new Vector2(1920 / 2, 1080 / 2);
}
if (texWidth < texHeight)
{
GetComponent<RectTransform>().sizeDelta = new Vector2(1080 / 2, 1920 / 2);
}
}
}
}
Related
I am trying to send the camera stream from unity to an RTCPeerConnection in the browser. The signaling between the two peers is done correctly, although when I send the MediaStreamTrack it does not work in the browser. Does anybody have an idea? This is similar to Unity.RenderStreaming, but I am trying to build my own. Here's the code I use for accessing the camera and sending the track.
//Create local peer
RTCConfiguration config = default;
config.iceServers = new[] { new RTCIceServer { urls = new[] { "stun:stun.l.google.com:19302" } } };
localConnection = new RTCPeerConnection(ref config);
localConnection.OnNegotiationNeeded = () => { Debug.Log("negotiation needed"); StartCoroutine(handleNegotiationNeeded()); };;
localConnection.OnIceCandidate += handleIceCandidate;
localConnection.OnIceConnectionChange = handleIceConnectionChange;
RenderTexture rt;
if (arCamera.targetTexture != null)
{
rt = arCamera.targetTexture;
RenderTextureFormat supportFormat = WebRTC.WebRTC.GetSupportedRenderTextureFormat(SystemInfo.graphicsDeviceType);
GraphicsFormat graphicsFormat = GraphicsFormatUtility.GetGraphicsFormat(supportFormat, RenderTextureReadWrite.Default);
GraphicsFormat compatibleFormat = SystemInfo.GetCompatibleFormat(graphicsFormat, FormatUsage.Render);
GraphicsFormat format = graphicsFormat == compatibleFormat ? graphicsFormat : compatibleFormat;
if (rt.graphicsFormat != format)
{
Debug.LogWarning(
$"This color format:{rt.graphicsFormat} not support in unity.webrtc. Change to supported color format:{format}.");
rt.Release();
rt.graphicsFormat = format;
rt.Create();
}
arCamera.targetTexture = rt;
}
else
{
RenderTextureFormat format = WebRTC.WebRTC.GetSupportedRenderTextureFormat(SystemInfo.graphicsDeviceType);
rt = new RenderTexture(1270, 720, 0, format)
{
antiAliasing = 1
};
rt.Create();
arCamera.targetTexture = rt;
}
track = new VideoStreamTrack(rt);
Debug.Log(rt.format);
Debug.Log(track.Texture.graphicsFormat);
Debug.Log(track.IsEncoderInitialized);
localConnection.AddTrack(track);
Debug.Log("track added");
The full code can be found here
https://github.com/MohammedShetaya/AR_Video_Straming/blob/main/Assets/Scripts/WSClient.cs
yes it is possible, but it is quite difficult, and beyond the scope of a SO answer. Check out this Unity Package for some help: https://docs.unity3d.com/Packages/com.unity.webrtc#2.4/manual/index.html
All buttons and text can show well but the image just shows only last button. when I try to click to check user.value.imageUrl, it's correct. I don't know what is wrong, please help me. I think IEnumerator has something I am missing.
my code is:
void CreateButton()
{
FirebaseRestClent.GetUsers(users =>
{
foreach (var user in users)
{
StartCoroutine(downloadImage(user.Value.imageUrl));
var clone = Instantiate(button,m_transform) as GameObject;
var modelBtn = clone.GetComponent<Button>();
imageToUpdate = modelBtn.GetComponent<Image>();
title = modelBtn.GetComponentInChildren<Text>();
title.text = user.Value.saveFile;
//Debug.Log($"{user.Value.modelUrl} {user.Value.saveFile} {user.Value.imageUrl}");
modelBtn.onClick.AddListener(delegate {
Debug.Log(user.Value.modelUrl + user.Value.saveFile + user.Value.imageUrl);
dlManager.PlayModel(user.Value.modelUrl, user.Value.saveFile,user.Value.musicUrl,false);
//dlManager.PlayModel(user.Value.modelUrl, user.Value.saveFile, false);
});
}
});
}
IEnumerator downloadImage(string url)
{
UnityWebRequest www = UnityWebRequestTexture.GetTexture(url);
DownloadHandler handle = www.downloadHandler;
//Send Request and wait
yield return www.SendWebRequest();
if (www.isHttpError || www.isNetworkError)
{
UnityEngine.Debug.Log("Error while Receiving: " + www.error);
}
else
{
UnityEngine.Debug.Log("Success");
//Load Image
Texture2D texture2d = DownloadHandlerTexture.GetContent(www);
Sprite sprite = null;
sprite = Sprite.Create(texture2d, new Rect(0, 0, 326, texture2d.height), Vector2.zero);
imageToUpdate.sprite = sprite;
}
}
you always overwrite the same imageToUpdate field reference in your loop so most probably all results get written into the Image component of the very last user.
You should rather simply pass the image reference on to the routine
void CreateButton()
{
FirebaseRestClent.GetUsers(users =>
{
foreach (var user in users)
{
var clone = Instantiate(button, m_transform) as GameObject;
var modelBtn = clone.GetComponent<Button>();
var image = modelBtn.GetComponent<Image>();
// instead of storing it in a class field rather
// pass the image reference into the Coroutine
StartCoroutine(downloadImage(user.Value.imageUrl, image));
title = modelBtn.GetComponentInChildren<Text>(true);
title.text = user.Value.saveFile;
modelBtn.onClick.AddListener(()=>
{
Debug.Log(user.Value.modelUrl + user.Value.saveFile + user.Value.imageUrl);
dlManager.PlayModel(user.Value.modelUrl, user.Value.saveFile, user.Value.musicUrl, false);
});
}
});
}
IEnumerator downloadImage(string url, Image targetImage)
{
using(www = UnityWebRequestTexture.GetTexture(url))
{
//Send Request and wait
yield return www.SendWebRequest();
if (www.isHttpError || www.isNetworkError)
{
Debug.Log("Error while Receiving: " + www.error);
}
else
{
Debug.Log("Success");
//Load Image
var texture2d = DownloadHandlerTexture.GetContent(www);
var sprite = Sprite.Create(texture2d, new Rect(0, 0, 326, texture2d.height), Vector2.zero);
targetImage.sprite = sprite;
}
}
}
I am using Parse for a game that I'm developing.
Everything is ok until I try to uplad a file, no matters his extension I always get this Error : "get_version can only be called from main thread"
URL=http://www.hostingpics.net/viewer.php?id=764075parseError.png
and this is my script :
byte[] data = System.Text.Encoding.UTF8.GetBytes("Working at Parse is great!");
ParseFile file = new ParseFile("resume.txt", data);
Task saveTask = file.SaveAsync();
var player = new ParseObject ("FilesLibrary");
player ["Number"] = 155;
player ["Files"] = file;
saveTask = player.SaveAsync();
I have tried to place this script in different places but I always get the same problem.
You need to make sure the file is uploaded to Parse first, before you try to save it to the db object.
The Parse Unity documentation is not very good as it doesn't fit Unity's threading model. I would suggest using co-routines to do all your Parse calls.
I posted this on Unity Answers as well, but you get the idea..
// The file first needs to be uploaded to Parse before it can be saved to the object in the database
ParseFile _playerFile;
public void CreatePlayer()
{
StartCoroutine (UploadPlayerFile ((response) => {
if(response == 1)
StartCoroutine(CreateProjectAsync());
else
Debug.LogError("The file could not be uploaded");
}));
}
IEnumerator UploadPlayerFile(Action <int> callback)
{
var fileBytes = System.IO.File.ReadAllBytes (#"C:\myfile.jpg");
_playerFile = new ParseFile ("file.jpg", fileBytes, "image/jpeg");
var saveTask = _playerFile.SaveAsync ();
while (!saveTask.IsCompleted)
yield return null;
if (saveTask.IsFaulted) {
Debug.LogError ("An error occurred while uploading the player file : " + saveTask.Exception.Message);
callback (-1);
} else {
callback (1);
}
}
IEnumerator CreateProjectAsync()
{
ParseObject player = new ParseObject ("Player");
player ["Number"] = 111;
player ["Files"] = _playerFile;
var saveTask = player.SaveAsync ();
while (!saveTask.IsCompleted)
yield return null;
if (saveTask.IsFaulted) {
Debug.LogError ("An error occurred while creating the player object : " + saveTask.Exception.Message);
} else {
Debug.Log ("Player created successfully");
}
}
I guess you need force your Parse queries to execute on Main Thread. you can achieve this by
public readonly static Queue<Action> ExecuteOnMainThread = new Queue<Action> ();
Add your query Method in this Queue. Like
ExecuteOnMainThread.Enqueue (() => {
StartCoroutine (SaveData (ID, playerName)); });
In your SaveData() Coroutine you need to execute your Parse queries.
and finally:
void Update()
{
while (ExecuteOnMainThread != null && ExecuteOnMainThread.Count > 0)
{
Action currentAction = ExecuteOnMainThread.Dequeue();
if (currentAction != null)
{
currentAction.Invoke ();
}
}
}
Weird, the same script works fine with unity 4.6 but not 5.1 !
I am trying to integrate FB SDK in my UNITY GAME for ios and ANDROID.
FB SHARE AND INVITE are working fine which are in "FBSCENE" scene.
But in "GAME" scene i want to take a SCREENSHOT after game is finished and post it on users FB WALL.
I read "Take and publish a screenshot" under EXAMPLE at https://developers.facebook.com/docs/unity/reference/current/FB.API
and my code is this
public void screenShareFB()
{
callFB ();
}
void callFB () {
if (!FB.IsLoggedIn) {
FB.Login("email,publish_actions", LoginCallback);
}
else {
StartCoroutine(TakeScreenshot());
}
}
void LoginCallback(FBResult result) {
FbDebug.Log("LoginCallback");
if (FB.IsLoggedIn) {
StartCoroutine(TakeScreenshot());
}
}
/*
void OnLoggedIn() {
FbDebug.Log("Logged in. ID: " + FB.UserId);
}*/
private IEnumerator TakeScreenshot() {
yield return new WaitForEndOfFrame();
var width = Screen.width;
var height = Screen.height;
var tex = new Texture2D(width, height, TextureFormat.RGB24, false);
// Read screen contents into the texture
tex.ReadPixels(new Rect(0, 0, width, height), 0, 0);
tex.Apply();
byte[] screenshot = tex.EncodeToPNG();
var wwwForm = new WWWForm();
wwwForm.AddBinaryData("image", screenshot, "InteractiveConsole.png");
wwwForm.AddField("message", "herp derp. I did a thing! Did I do this right?");
FB.API("me/photos", Facebook.HttpMethod.POST, Callback, wwwForm);
Debug.Log("done");
}
private Texture2D lastResponseTexture;
private string lastResponse = "";
public string ApiQuery = "";
void Callback(FBResult result)
{
lastResponseTexture = null;
if (result.Error != null)
lastResponse = "Error Response: " + result.Error;
else if (!ApiQuery.Contains("/picture"))
lastResponse = "Success Response: " + result.Text;
else
{
lastResponseTexture = result.Texture;
lastResponse = "Success Response: ";
}
}
screenShareFB() being called when user tap on SHARE SCREENSHOT button.
NOTE:
I have called FB.Init(with a call back method); in Start() of the script.
I am using C# script.
Is this because my app is not live on facebook ?
I have configured it on facebook already.
I was testing on PC.
Please tell me if anything is wrong with the code or any thing i am missing.
The problem was permissions.
the "publish_actions" permission was not review by facebook.
thus the screenshot was not getting published.
but 1 thing when try to fill details for fb to review my app and allow me to access publish_actions for my user.
They require us to add a "platform" under "SETTINGS" in "BASIC".
Now i am using UNITY.
which is not mentioned as a platform there.
What should i do.?
I have been developing a game in Unity3D in C#. So I have set the game to upload the game save file
to Parse every 2 minutes. When I run the code in Unity it works fine, it saves the game save file locally, and every 2 minutes it uploads it to Parse, it also points to the user who's playing the game. Although when I run the game on a mobile device the file does not upload, I have been refreshing my parse data object in the last half an hour and I still haven't got anything.
Here is the code:
void Start ()
{
if (ParseUser.CurrentUser != null)
{
gapTest = true;
}
}
void Update ()
{
if(gapTest)
{
StartCoroutine(UploadFile());
}
if (uploadSaveFile)
{
OnZipComplete();
uploadSaveFile = false;
}
else if (createNewSaveFile)
{
CreateNewSaveFile();
createNewSaveFile = false;
}
}
IEnumerator UploadFile()
{
gapTest = false;
yield return new WaitForSeconds (120.0F);
if(ParseUser.CurrentUser != null)
{
OnZipComplete();
}
gapTest = true;
}
void OnZipComplete()
{
var query = ParseObject.GetQuery("GameSave").WhereEqualTo("UserObjectId", ParseUser.CurrentUser);
if (existingGameSave == null)
{
query.FindAsync().ContinueWith(t =>
{
IEnumerable<ParseObject> results = t.Result;
int resultCount = 0;
foreach (var result in results)
{
if (resultCount > 0)
{
Debug.LogError("Found more than one save file for user!");
}
else
{
resultCount++;
existingGameSave = result;
uploadSaveFile = true;
}
}
if (resultCount == 0)
{
createNewSaveFile = true;
}
});
}
else
{
UpdateExistingSaveFile();
}
}
private void CreateNewSaveFile()
{
//upload the file to parse
zipPath = Application.persistentDataPath + "/SaveGame.zip";
byte[] data = System.Text.Encoding.UTF8.GetBytes(zipPath);
ParseFile GameSave = new ParseFile("GameSave.zip", data);
var gameSave = new ParseObject("GameSave");
gameSave["UserObjectId"] = ParseUser.CurrentUser;
gameSave["GameSaveFile"] = GameSave;
Task saveTask = gameSave.SaveAsync();
Debug.Log("New Game save file has been uploaded");
}
void UpdateExistingSaveFile()
{
//upload the file to parse
UserIdFile = Application.persistentDataPath + "/UserId.txt";
zipPath = Application.persistentDataPath + "/SaveGame.zip";
byte[] data = System.Text.Encoding.UTF8.GetBytes(zipPath);
ParseFile GameSave = new ParseFile("GameSave.zip", data);
existingGameSave["GameSaveFile"] = GameSave;
Task saveTask = existingGameSave.SaveAsync();
Debug.Log("Existing Game save file has been uploaded");
}
This might be a problem related to permission on android device.
Go to Build Setting > Player Setting > Other Settings, and check "Write Access", by default, it is internal Only which means that the path is for development purpose only.
Try Setting the Write Access as External(SD Card) or add WRITE_ EXTERNAL_STORAGE permission into AndroidManifest.xml. And check Android/file/com.your.appid/files of your sd card if your data is actually getting written on device.
If data is getting written, then it will sure get uploaded on parse.com.