This question already has answers here:
Unity - need to return value only after coroutine finishes
(3 answers)
Closed 5 years ago.
Hi im trying to implement saving/loading from sql database into my game.
i have it all working apart from the fact that i need to hit load twice for it to load the data.
the code to set the data finishes before i have a chance to set it.
public void loadData()
{
GetComponent<SavingLoading>().Load(GameObject.Find("LoginSystem").GetComponent<LoginSystem>().Username);
if (GetComponent<SavingLoading>().LoadedData != "")
{
string[] Data = GetComponent<SavingLoading>().LoadedData.Split(',');
Level = Convert.ToInt32(Data[0]);
CurrentXp = Convert.ToInt32(Data[1]);
currentHealth = Convert.ToInt32(Data[2]);
maxHealth = Convert.ToInt32(Data[3]);
Vector3 LoadedPos = new Vector3(Convert.ToSingle(Data[4]), Convert.ToSingle(Data[5]), Convert.ToSingle(Data[6]));
transform.position = LoadedPos;
}
}
Theese functions are in another script.
public void Load(string name)
{
StartCoroutine(LoadCall(name));
}
IEnumerator LoadCall(string name)
{
WWWForm form = new WWWForm();
form.AddField("name", name);
WWW www = new WWW(LoadPhP, form);
yield return www;
string _return = www.text;
LoadedData = _return;
}
how can i go about only updating the data if there is data present? without having to press the load button twice.
According to the docs, you should wrap the www variable in a using statement in order to properly disposeit. Then yield return on it, like so:
using (WWW www = new WWW(url))
{
yield return www;
string _return = www.text;
LoadedData = _return;
}
I am not sure what you mean by 'only updating the data if there is data present'. Do you have more code to show that might help to clarify your intent?
Related
I'd first like to apologise beforehand if my question is very basic and dumb as I am very new to coding and unity/vuforia in general.
I'd like to track a custom image and display a quad under it (i've done this part). I've also managed to set the material/texture/look of the quad to a image located in my computer. However, when trying to do build into a android app, I am unable to set the file path correctly and it doesnt work.
I would like the material/texture/look of the quad to be constantly changing/updating, is this possible? I can constantly overwrite the image file on my computer/phone but will the photo update in the app itself?
Thank you so much for any help in advance!
A desperate student
void Start()
{
string path = "file://storage/emulated/0/Android/data/com.kenny.argame/files/im2.jpg";
StartCoroutine(DownloadImage(path));
}
IEnumerator DownloadImage(string MediaUrl)
{
GetComponent<Renderer>().material = FinalMaterialRef;
UnityWebRequest request = UnityWebRequestTexture.GetTexture(MediaUrl);
yield return request.SendWebRequest();
if (request.isNetworkError || request.isHttpError)
Debug.Log(request.error);
else
_material.mainTexture = ((DownloadHandlerTexture)request.downloadHandler).texture;
FinalMaterialRef = _material;
}
}
While it's not clear from your code where _material and FinalMaterialRef are originally declared -- they're not in the scope you showed -- you might have your references mixed up and overwrite them, but not the actual material. This works:
using System.Collections;
using UnityEngine;
using UnityEngine.Networking;
public class ChangeTexture : MonoBehaviour
{
Material material = null;
void Start()
{
material = GetComponent<Renderer>().material;
const string path = "file://E:/_temp/1.jpg";
StartCoroutine(DownloadAndAssignImage(path));
}
IEnumerator DownloadAndAssignImage(string mediaUrl)
{
UnityWebRequest request = UnityWebRequestTexture.GetTexture(mediaUrl);
yield return request.SendWebRequest();
if (request.isNetworkError || request.isHttpError)
{
Debug.Log(request.error);
}
else
{
material.mainTexture =
((DownloadHandlerTexture)request.downloadHandler).texture;
}
}
}
I've taken over development of an app that is complete minus a bug requiring an internet connection to load images as it doesn't access them from the cache despite an attempt to do so.
Can anyone help me figure out what is going wrong in the following?
public class SpriteCache : Singleton<SpriteCache>
{
Dictionary<string, Sprite> _cache = new Dictionary<string, Sprite>();
public void LoadSprite(string url, Action<Sprite> callback)
{
StartCoroutine(LoadSpriteCoroutine(url, callback));
}
public IEnumerator LoadSpriteCoroutine(string url, Action<Sprite> callback)
{
if (_cache.ContainsKey(url))
{
callback(_cache[url]);
yield break;
}
var www = new WWW(url);
while (!www.isDone)
{
yield return www;
}
if (!string.IsNullOrEmpty(www.error))
{
Debug.LogErrorFormat("Tried to obtain texture at '{0}' but received error '{1}'", url, www.error);
yield break;
}
var texture = www.texture;
if (texture == null)
{
Debug.LogErrorFormat("No texture found at '{0}'!", url);
yield break;
}
var sprite = Sprite.Create(texture, new Rect(0, 0, texture.width, texture.height), new Vector2(texture.width / 2, texture.height / 2));
_cache[url] = sprite;
callback(sprite);
}
edit:
A further explanation to clarify
var www = new WWW(url)
This grabs the images stored on a server which works, as far as I'm aware after one instance of grabbing the image this should place the item in cache for use later.
I've tried using the following updated method to see if that would fix it.
var www = WWW.LoadFromCacheOrDownload(url, 1)
This resulted in it not working in any capacity and never changing the images from the placeholders.
The first if statement in the "LoadSpriteCoroutine" is supposed to catch if the sprite already exists in the "_cache" Dictionary by checking if there is a key for the url, which there should be after the first running instance with and internet connection
Load image from _cache if its in there:
if (_cache.ContainsKey(url))
{
callback(_cache[url]);
yield break;
}
Add image to _cache if it wasn't previously in there:
var sprite = Sprite.Create(texture, new Rect(0, 0, texture.width, texture.height), new Vector2(texture.width / 2, texture.height / 2));
_cache[url] = sprite;
So I figured out a solution,
all the other methods of saving images to the cache seemed to fail for iOS, I removed all of the previous "_cache" stuff.
this function for saving worked for me:
//if file doesn't exist then save it for fututre reference
if (!File.Exists(_FileLocation + texName))
{
byte[] bytes;
if (Path.GetExtension(texName) == ".png")
{
bytes = texture.EncodeToPNG();
}
else
{
bytes = texture.EncodeToJPG();
}
File.WriteAllBytes(Application.persistentDataPath + texName, bytes);
}
that section is placed after the "callback(sprite)".
The check for saved items is placed where the old "if(_cache.ContainsKey(url))" section was
texName = Path.GetFileName(texName);
if (File.Exists( _FileLocation + texName))
{
url = _FileLocation + "/"+ texName;
}
please note that the "LoadSpriteCoroutine" now take a extra string argument 'texName' in its calls which is just the a shortened instance of the url which it then cuts to just the file name and extension. If it finds a match then it replaces url with a the persistent file path + the fileName to grab it locally opposed to over the network as normal.
_FileLocation is defined as follows
string _FileLocation;
public void Start()
{
_FileLocation = Application.persistentDataPath;
}
The reason behind referencing it in that method is to save performance from calling the address though application AND if you were working on a cross platform solution then the data path for saving may need to be changed as such you could put a detection if or case statement to change _FileLocation to suit your needs.
I got this php on my server, that is called to run when I click on a button to add currency:
// Select player_name
$check = mysqli_query($conn,"select Currency from player where Name='$player_name'");
$checkrows=mysqli_num_rows($check);
if($checkrows==1) {
$sql = "INSERT INTO player (Currency) VALUES ('post_player_currency')";
$result = mysqli_query($conn, $sql) or die('Error querying database.');
echo ("DATABASE: Currency added ");
} else {
// Insert player into database
echo "DATABASE: Currency NOT added ";
}
?>
And on my Unity C# script, I have this:
public IEnumerator AddCurrency(Text playername, Text playercurrency) {
_tempCoins = int.Parse(coins.text);
_tempCoins += 10;
coins.text = _tempCoins.ToString();
WWWForm form = new WWWForm ();
form.AddField ("post_player_name", playername.text);
form.AddField ("post_player_currency", playercurrency.text);
WWW www = new WWW (RegisterPlayerURL, form);
yield return www;
print (www.text);
if (www.text == "DATABASE: Currency added ") {
print ("TRANSACTION: Current Coins: " + _tempCoins);
}
}
The scripts themselves are working, but what I don't understand is why I get Error querying database. everytime as result, instead of it actually adding the value to the database.
A very similar script to register a player to the database that I made, is working just fine. I've spent a lot of time and I cannot figure out why this one doesn't work.
Help is appreciated! Thanks!
I am using Unity3d (c#) with facebook API. I try to show my profile image on the screen when I run it on the unity simulator everything works perfect and I got the image. but when I build a version and try it on a real device I get nothing.
I know that there is a issue with facebook CDN. the result of the request is 302 and WWW class can't handle with redirects so I did something like this (c# is a new language for me):
WWW www = new WWW(url);
yield return www;
if( www.responseHeaders.ContainsKey( "LOCATION" ) ){
var redirection = www.responseHeaders[ "LOCATION" ];
WWW wwwRe = new WWW( redirection );
yield return wwwRe;
callback( wwwRe.texture, userID );
}else{
callback( www.texture, userID );
}
please help me I loose my mind, why all my personal data comes on the device except the image that works perfect in unity. what I did wrong?
thanks.
The solution:
I tried many options to get profile image on the device nothing worked.
finally I upgrade Facebook SDK from 6.2.1 to 6.2.2 and Unity from 5.1 to 5.1.3 remove the app again from the device ( clear all the data ) and it works. it looks like Facebook's issue ( this is not the first time that they release SDK with bugs ).
I accept Umair's answer even that his code have some syntax issues, he really tried to help me and basically his answer is right.
I used this code to test my image:
( hope it will help for someone )
private void getMyProfileData(){
// get profile image.
FB.API( Util.GetPictureURL( "me", 128, 128 ), Facebook.HttpMethod.GET, callBackGetProfilePicture );
}
private void callBackGetProfilePicture( FBResult result ){
// in case if there some error with the image.
if( result.Error != null ){
// call this method again
}
string ImageUrl = Util.DeserializePictureURLString( result.Text );
StartCoroutine(LoadPictureCoroutune( ImageUrl ));
}
IEnumerator LoadPictureCoroutune(string url){
var profilePicRequest = new WWW( (string)url );
yield return profilePicRequest;
if( profilePicRequest.error == null)
{
Texture2D profilePic = profilePicRequest.texture;
// my test image place
Image profileImage = FBavatar.GetComponent<Image>();
profileImage.sprite = UnityEngine.Sprite.Create( profilePic, new Rect(0,0,128,128), new Vector2( 0, 0 ));
}
else
{
Debug.LogError("Error While downloading Picture: " + profilePicRequest.error);
}
}
Use following Code to get Picture data from Facebook API:
FB.API ("/v2.4/me/?fields=picture", Facebook.HttpMethod.GET, RequestResponce);
In RequestResponce method:
void RequestResponce(FBResult result)
{
Debug.Log("requestResponce(): " + result.text);
if (!string.IsNullOrEmpty(result.Error))
{
Debug.Log("FB_API:ReadRequest:Failed:"+result.Error);
}
else
{
var data = SimpleJSON.JSON.Parse(result.Text);
string url = data["picture"]["data"]["url"];
StartCoroutine(LoadPictureCoroutune(url));
}
}
IEnumerator LoadPictureCoroutune(string url)
{
var profilePicRequest = new WWW(url);
Debug.Log("Going to Load Picture ");
yield return profilePicRequest;
if(profilePicRequest.error == null)
{
Texture2D profilePic = profilePicRequest.texture;
if(profilePic != null)
{
// Use ProfilePic to wherever you want to.
//SetPicture(ProfilePic);
}
}
else
{
Debug.LogError("Error While downloading Picture: " + profilePicRequest.error);
}
}
I'm trying to access a Json script, it is in a domain. The script holds a couple of images, which are hyperlinked. I've tried using WWW to access the script, but the image i received was a picture of a huge red question mark. Clearly I'm going about the wrong way with this. So i'm assuming I'm supposed to decode the json script through unity and then display the image with the ability to see next/previous image by clicking? I'm unfamiliar with Json so how about would i call the script and read the the images it's calling?
This is what my code looks like, the code works since I've tried another non Json domain with just an image- and it works perfectly fine.
void Start ()
{
renderer.material.mainTexture = new Texture2D(4,4, TextureFormat.DXT1, false);
url = "http://hosting.xivmedia.com/JsonHome/JSON/more_games.json";
www = new WWW(url);
StartCoroutine(WaitForSeconds(www));
}
IEnumerator WaitForSeconds(WWW www)
{
yield return www;
www.LoadImageIntoTexture(renderer.material.mainTexture as Texture2D);
if (www.error == null)
{
Debug.Log("WWW Ok!: " + www.data);
imageLoaded = true;
}
else
{
Debug.Log("WWW Error: " + www.error);
}
}
void OnGUI()
{
GUI.DrawTexture(new Rect(20, 80, 100, 100), renderer.material.mainTexture as Texture2D, ScaleMode.StretchToFill);
}
Yes, as you said correctly, you'll need to decode the JSON script first. You'll need a JSON parser to do that. The one I highly recommend is JSON .NET For Unity, but MiniJSON should do just fine.
So, assuming your json is just a list of URLs as so:
[
"http://.....",
"http://....."
]
With JSON.NET you would modify your code as follows:
IEnumerator WaitForSeconds(WWW www)
{
yield return www;
string json = www.text;
List<string> imageUrls = JsonConvert.Deserialize<List<string>>(json);
foreach (string imageUrl in imageUrls)
{
WWW imageWWW = new WWW(imageUrl);
imageWWW.LoadImageIntoTexture(renderer.material.mainTexture as Texture2D);
if (imageWWW .error == null)
{
Debug.Log("WWW Ok!: " + imageWWW.data);
imageLoaded = true;
}
else
{
Debug.Log("WWW Error: " + imageWWW.error);
}
}
}
If the JSON is more complicated, you can create a class that the JSON can deserialize to. I.e., if the JSON is a list of objects as so:
[
{
"name": "test",
"url" : "http://...."
},
{
"name": "something",
"url" : "http://..."
}
]
Then you would have a simple class
public class ImageData
{
public string name;
public string url;
}
And deserialize the JSON as so
List<ImageData> imageUrls = JsonConvert.Deserialize<List<ImageData>>(json);
foreach (ImageData data in imageUrls)
{
WWW imageWWW = new WWW(data.url);
imageWWW.LoadImageIntoTexture(renderer.material.mainTexture as Texture2D);