Instantiate game object loaded from assetbundle lost animation clip - c#

I Had all my Npc's prefab packed into assetbundle with all dependencies,but when I finished loading all dependencies of one npc and instantiate it into scene, some times the animation clip on it can't be played. I print animator.GetCurrentAnimatorClipInfo(0).Length and I found it is 0.So it seems that I had lost all animation clip of this animator.
The funny thing is that it doesn't happed on all npc,and I can't find any differences between them.
I used following codes to attached assetbundle name to all assets.
private void SelectionAndAssetsListChange()
{
EditorUtility.ClearProgressBar();
selection = Selection.objects;
//assets = new AssetImporter[selection.Length];
if (selection.Length > 0)
{
for (int i = 0; i < selection.Length; i++)
{
string assetPath = AssetDatabase.GetAssetPath(selection[i]);
string[] dps = AssetDatabase.GetDependencies(assetPath);
int length = dps.Length;
int index = 0;
foreach (var dp in dps)
{
if (EditorUtility.DisplayCancelableProgressBar("Changing Assets's ab name ", "Changing No." + index + "/" + length, (float)index / length))
{
EditorUtility.ClearProgressBar();
return;
}
if (dp.EndsWith(".cs"))
continue;
AssetImporter dpAsset = AssetImporter.GetAtPath(dp);
string assetNameDPs = dp.Substring("Assets".Length + 1);
assetNameDPs = assetNameDPs.Replace(Path.GetExtension(assetNameDPs), ".data");
assetNameDPs = Path.Combine("assetbundle", assetNameDPs);
assetNameDPs = assetNameDPs.Replace("\\", "/");
dpAsset.assetBundleName = assetNameDPs;
index++;
}
EditorUtility.ClearProgressBar();
}
}
}
Following Codes are used to load assetbundle ,i used 2 dictionaries TempABGO_Dict and TempDPSAB_Dict to
make sure make sure it doesn't double load.
IEnumerator SingleTempGOABLoad(string abName)
{
string path = Application.streamingAssetsPath + "/" + abName;
if (!File.Exists(path))
{
Debug.Log("Can't find path:" + path);
yield break;
}
Debug.Log(" 1 SingleTempGOABLoad :" + abName);
if (TempABGO_Dict.ContainsKey(abName))
yield break;
string[] allDps = mainManifest.GetAllDependencies(abName);
for (int i = 0; i < allDps.Length; i++)
if (TempDPSAB_Dict.ContainsKey(allDps[i]))
{
TempDPSAB_Dict[allDps[i]].refCount++;
}
else
{
DpsContainer dc = new DpsContainer(null, allDps[i]);
TempDPSAB_Dict[allDps[i]] = dc;
StartCoroutine(SingleDpsABLoad(allDps[i]));
yield return 0;
}
for (int i = 0; i < allDps.Length; i++)
yield return new WaitUntil(() => TempDPSAB_Dict[allDps[i]].finishLoad);
yield return 0;
AssetBundleCreateRequest ab = AssetBundle.LoadFromFileAsync(path);
yield return ab;
GameObject go = null;
AssetBundleRequest abReq = ab.assetBundle.LoadAllAssetsAsync<GameObject>();
yield return abReq;
go = (GameObject)abReq.asset;
TempABGO_Dict[abName] = new TempPrefabABPair(ab.assetBundle, go, allDps);
}
IEnumerator SingleDpsABLoad(string abName)
{
DpsContainer dc = TempDPSAB_Dict[abName];
AssetBundleCreateRequest ab = null;
ab = AssetBundle.LoadFromFileAsync(Application.streamingAssetsPath + "/" + abName);
yield return ab;
dc.AB = ab.assetBundle;
AssetBundleRequest rq = ab.assetBundle.LoadAllAssetsAsync();
yield return rq;
dc.finishLoad = true;
}
This is what I used to build BuildAssetBundles.
BuildPipeline.BuildAssetBundles(Application.streamingAssetsPath, compressMode, EditorUserBuildSettings.activeBuildTarget);

First more a general note, you can use
yield return null;
and you can also probably reduce the amount of those a lot e.g. before and after
AssetBundleCreateRequest ab = AssetBundle.LoadFromFileAsync(path);
within the for loop etc.
And then within in your SingleDpsABLoadyou do not wait for the
ab = AssetBundle.LoadFromFileAsync(....);
to actually finish! You just continue to immediately set
dc.finihed = true
so you might just be lucky that sometimes the rest of the redundant
yield return 0;
in the for loop etc are just delay enough to actually manage to load it in time ^^
So you probably could rather do something like
IEnumerator SingleDpsABLoad(DpsContainer dpsContainer)
{
var path = Path.Combine(Application.streamingAssetsPath, dpsContainer.abName);
if (!File.Exists(path))
{
Debug.LogError("Can't find path:" + path);
yield break;
}
var ab = AssetBundle.LoadFromFileAsync(path);
// actually wait until the async operation is done
yield return ab;
// can also make that one async
yield return ab.assetBundle.LoadAllAssetsAsync();
dpsContainer.AB = ab.assetBundle;
dpsContainer.finishLoad = true;
}
IEnumerator SingleTempGOABLoad(string abName)
{
Debug.Log(" 1 SingleTempGOABLoad :" + abName);
// personally I would start with that check here already
var path = Path.Combine(Application.streamingAssetsPath, abName);
if (!File.Exists(path))
{
Debug.Log("Can't find path:" + path);
yield break;
}
if (TempABGO_Dict.ContainsKey(abName)) yield break;
var allDps = mainManifest.GetAllDependencies(abName);
foreach (var dps in allDps)
{
if (TempDPSAB_Dict.TryGetValue(dps, out var dpsContainer))
{
dpsContainer.refCount++;
}
else
{
dpsContainer = new DpsContainer(null, dps);
TempDPSAB_Dict[dps] = dpsContainer;
StartCoroutine(SingleDpsABLoad(dps));
}
}
// using System.Linq
yield return new WaitUntil(() => TempDPSAB_Dict.All(kvp => kvp.Value.finishLoad));
var ab = AssetBundle.LoadFromFileAsync(path);
yield return ab;
var abReq = ab.assetBundle.LoadAllAssetsAsync<GameObject>();
yield return abReq;
var go = (GameObject)abReq.asset;
TempABGO_Dict[abName] = new TempPrefabABPair(ab.assetBundle, go, allDps));
}

Related

During debugging I have noticed my code breaks when loading assets or while instantiating gameObject in for loop. There is no error in the code

I wanted to instantiate GameObject with UI elements and wanted to populate data in it.
I have tried running the code in the for loop in separate function or coroutine. I have also tried to copy for loop inside the Firebase logic. Even then it's not working. The only thing worked was when I put the code above the Firebase logic.
IEnumerator DoShowSomeProducts(Category category)
{
searchText.text = category.name;
foreach (Transform each in GetComponentInChildren<LayoutGroup>().transform)
{
GameObject.Destroy(each.gameObject);
}
// parse product
List<Item> productsInCategory = new List<Item>();
//Tried the for loop logic here works well but didn't need it here.
FirebaseDatabase.DefaultInstance.GetReference("Product").GetValueAsync().ContinueWith(task =>
{
if (task.IsFaulted)
{
Debug.Log("There is a fault");
}
else if (task.IsCompleted)
{
DataSnapshot snap = task.Result;
if (snap.ChildrenCount > 0)
{
foreach (var child in snap.Children)
{
print("Creating Item");
Item _item = new Item();
_item.url = child.Child("url").Value.ToString();
_item.image = child.Child("img").Value.ToString();
_item.code = child.Child("code").Value.ToString();
_item.price = child.Child("price").Value.ToString();
_item.category = child.Child("category").Value.ToString();
_item.descr = child.Child("descr").Value.ToString();
_item.link = child.Child("link").Value.ToString();
_item.name = child.Child("name").Value.ToString();
print("================================= \n Product\n" + _item.url + "\n" + _item.image + "\n" + _item.code + "\n" + _item.price
+ "\n" + _item.category + "\n" + _item.descr + "\n" + _item.link + "\n" + _item.name + "\n===========================");
productsInCategory.Add(_item);
//Here tried to run the for loop logic using separate functions and co-routines.
print("Added Product");
Debug.Log(_item.category);
}
}
}
});
for (int i = 0; i < productsInCategory.Count; i++)
{
print("PinCat Count = " + productsInCategory.Count);
Item item = productsInCategory[i];
GameObject obj = Resources.Load<GameObject>("Prefabs/ProductButtonPrefab");//this line breaks the code
GameObject clone = GameObject.Instantiate(obj);//this line brreaks the code.
clone.transform.parent = GetComponentInChildren<LayoutGroup>().transform;
clone.transform.localScale = Vector3.one;
clone.GetComponent<ProductButton>().nameText.text = item.name;
clone.GetComponent<ProductButton>().product = item;
clone.GetComponent<ProductButton>().categoryText.text = item.category;
clone.GetComponent<ProductButton>().priceText.text = item.price;
clone.GetComponent<ProductButton>().nameText.text = item.name;
print("Know Downloading Image");
WWW www = new WWW(item.image);
yield return www;
clone.GetComponent<ProductButton>().mainImage.sprite = Sprite.Create(www.texture, new Rect(0, 0, www.texture.width, www.texture.height), new Vector2(0, 0));
}
yield break;
}
Your Coroutine doesn't wait until firebase has a result so the for loop is always executed before any results from firebase have reached
=> no items are generated since productsInCategory is always an empty list.
(also compare this post)
You should split up the two things into separate methods. Something like
public void DoShowSomeProducts(Category category)
{
searchText.text = category.name;
foreach (Transform each in GetComponentInChildren<LayoutGroup>().transform)
{
GameObject.Destroy(each.gameObject);
}
// parse product
List<Item> productsInCategory = new List<Item>();
//Tried the for loop logic here works well but didn't need it here.
FirebaseDatabase.DefaultInstance.GetReference("Product").GetValueAsync().ContinueWith(task =>
{
if (task.IsFaulted)
{
Debug.Log("There is a fault");
}
else if (task.IsCompleted)
{
DataSnapshot snap = task.Result;
if (snap.ChildrenCount > 0)
{
foreach (var child in snap.Children)
{
print("Creating Item");
Item _item = new Item();
_item.url = child.Child("url").Value.ToString();
_item.image = child.Child("img").Value.ToString();
_item.code = child.Child("code").Value.ToString();
_item.price = child.Child("price").Value.ToString();
_item.category = child.Child("category").Value.ToString();
_item.descr = child.Child("descr").Value.ToString();
_item.link = child.Child("link").Value.ToString();
_item.name = child.Child("name").Value.ToString();
print("================================= \n Product\n" + _item.url + "\n" + _item.image + "\n" + _item.code + "\n" + _item.price
+ "\n" + _item.category + "\n" + _item.descr + "\n" + _item.link + "\n" + _item.name + "\n===========================");
productsInCategory.Add(_item);
//Here tried to run the for loop logic using separate functions and co-routines.
print("Added Product");
Debug.Log(_item.category);
}
// pass the list to the download routine
StartCoroutine(DownloadImages(productsInCategory));
}
}
});
}
private IEnumerator DownloadImages(List<Item> productsInCategory)
{
for (int i = 0; i < productsInCategory.Count; i++)
{
print("PinCat Count = " + productsInCategory.Count);
Item item = productsInCategory[i];
GameObject obj = Resources.Load<GameObject>("Prefabs/ProductButtonPrefab");
GameObject clone = GameObject.Instantiate(obj);
clone.transform.parent = GetComponentInChildren<LayoutGroup>().transform;
clone.transform.localScale = Vector3.one;
clone.GetComponent<ProductButton>().nameText.text = item.name;
clone.GetComponent<ProductButton>().product = item;
clone.GetComponent<ProductButton>().categoryText.text = item.category;
clone.GetComponent<ProductButton>().priceText.text = item.price;
clone.GetComponent<ProductButton>().nameText.text = item.name;
print("Know Downloading Image");
WWW www = new WWW(item.image);
yield return www;
// you should always debug things
if (!string.IsNullOrEmpty(www.error))
{
Debug.LogErrorFormat(this, "Download failed for item {0} at index {1} due to: {2}", item.name, i, www.error);
continue;
}
clone.GetComponent<ProductButton>().mainImage.sprite = Sprite.Create(www.texture, new Rect(0, 0, www.texture.width, www.texture.height), new Vector2(0, 0));
}
}
Also you said // this line breaks the code for
GameObject obj = Resources.Load<GameObject>("Prefabs/ProductButtonPrefab");
this lines assumes your object resides in "Assets/Resources/Prefabs/ProductButtonPrefab" so make sure it is inside the Resources folder
(That the next line also fails is obvious since you can't Instantiate(obj) if obj is null due to the previous line)
But why loading it from Resources at all and everytime again? Either load it only once before the for loop (or even better on game start)
GameObject itemPrefab = Resources.Load<GameObject>("Prefabs/ProductButtonPrefab");
for (int i = 0; i < productsInCategory.Count; i++)
{
print("PinCat Count = " + productsInCategory.Count);
Item item = productsInCategory[i];
GameObject clone = GameObject.Instantiate(itemPrefab);
// ...
}
or diectly put it into the "normal" assets and reference it in a field like
public GameObject ItemPrefab;

Method never execute because of yield break statement, how to recover from it?

I have to read multiple xml files from specific folder and store in list, but whenever I used yield break; keyword, the method wasn't called. I am totally stuck here.
Below is my code:
private IEnumerable<SyncEntity> GetUpdatedItemsOfTypePagination(string folderPath)
{
int currentPage = 1;
string finishFilePath = Path.Combine(folderPath, "GetUpdateItemsOfType_Finish.xml");
while (true)
{
string xmlFileFullPath = Path.Combine(folderPath, $"GetUpdateItemsOfType{currentPage}.xml");
bool pageReadCompleted = false;
for (int i = 0; i < 1000; i++) //wait max time of 1,000*0.1 = 100 seconds
{
if (!File.Exists(xmlFileFullPath))
{
if (File.Exists(finishFilePath))
{
yield break;
}
Thread.Sleep(TimeSpan.FromSeconds(0.1));
continue;
}
List<SyncEntity> pageItems = GetUpdatedItemsPage(xmlFileFullPath);
pageReadCompleted = true;
foreach (var syncEntity in pageItems)
{
yield return syncEntity;
}
break;
}
if (!pageReadCompleted)
{
throw new ApplicationException("Timeout reached for GetUpdatedItems method...");
}
currentPage++;
}
}

Loop not finding elements after going to new page.StaleElementReferenceException

List<IWebElement> shittyBiz = new List<IWebElement>();
var myEles = driverGC.FindElements(By.CssSelector("div.search-result"));
for (int i = 0;i<=1000;i++){
myEles = driverGC.FindElements(By.CssSelector("div.search-result"));
foreach (IWebElement business in myEles)
{
driverGC.Manage().Timeouts().ImplicitlyWait(TimeSpan.FromSeconds(10));
var starRating = " ";
try
{
starRating = business.FindElement(By.CssSelector("div.biz-rating > div.i-stars")).GetAttribute("title");
}
catch (OpenQA.Selenium.NoSuchElementException)
{
MessageBox.Show("No stars");
continue;
}
starRating = Regex.Replace(starRating, #"[A-Za-z\s]", string.Empty);
float stars = float.Parse(starRating);
MessageBox.Show(stars.ToString());
if (stars <= 3)
{
//shittyBiz.Add(starRating);
MessageBox.Show("Shitty");
driverGC.Manage().Timeouts().ImplicitlyWait(TimeSpan.FromSeconds(5));
var bizName = business.FindElement(By.CssSelector(".biz-name"));
MessageBox.Show(bizName.Text);
shittyBiz.Add(bizName);
var bizLocation = business.FindElement(By.CssSelector(".secondary-attributes"));
MessageBox.Show(bizLocation.Text);
shittyBiz.Add(bizLocation);
}
else
{
driverGC.Manage().Timeouts().ImplicitlyWait(TimeSpan.FromSeconds(5));
MessageBox.Show("Too good");
}
}
try
{
driverGC.FindElement(By.CssSelector("div.arrange_unit > a.u-decoration-none")).Click();
continue;
}
catch (OpenQA.Selenium.NoSuchElementException)
{
MessageBox.Show("No more pages");
return;
//driverGC.Quit();
}
}
I can get the program to run fine the first time, but after it uses the try at the end, to go to the next page, i get the StaleElementReferenceException error almost immediately on the starRating in the first try. I have tried everything that I can think of, but not sure why it is throwing me that error.
When the DOM is refreshed or changed the driver loses all the elements it previously located. After moving to the new page myEles is not valid any more and that's why you get StaleElementReferenceException on business when you trying to use it to locate another element. The solution is to locate myEles each iteration and keep the location using indexes with for loop
int size = 1;
for (int i = 0; i < size; ++i)
{
var myEles = driverGC.FindElements(By.CssSelector("div.search-result"));
size = myEles.Count();
driverGC.Manage().Timeouts().ImplicitlyWait(TimeSpan.FromSeconds(10));
var starRating = " ";
try
{
starRating = myEles[i].FindElement(By.CssSelector("div.biz-rating > div.i-stars")).GetAttribute("title");
}
//...
// and don't forget to go back
driver.Navigate().Back();
}

How to disable formula error check in Excel programmatically

I try to set formula to Excel cell in C# in several steps, however during the process I set it, it seems Excel throws exception due to invalid formula. I wonder how can I turn off the formula error check in C#. thanks
Edit
The formula is too long, longer than 255 characters.
So I can't set formula in one step.
Have to set a short formula, then replace see http://netoffice.codeplex.com/discussions/402947
see code below
but I get an error in rng.Formula = onePart;
where Constants.CUT_LENGTH = 253, Constants.MAX_FORMULA_LENGTH = 255, Separator = "||"
I try to set EvaluateToError to false, still get an error
XLApp.ErrorCheckingOptions.InconsistentFormula = false;
XLApp.ErrorCheckingOptions.EvaluateToError = false;
SetFormula(rangeFunction, formula);
public static void SetFormula(Range rng, string origFormula)
{
int i = 0;
foreach (var onePart in CutStringIntoSubstrings(origFormula))
{
if(i==0)
{
rng.Formula = onePart;
i++;
}
else
{
rng.Replace(Constants.Separator, onePart);
}
}
}
public static IEnumerable<string> CutStringIntoSubstrings(string origFormula)
{
if (origFormula == null) yield return string.Empty;
if (string.IsNullOrEmpty(origFormula)) yield return string.Empty;
if (origFormula.Length <= Constants.MAX_FORMULA_LENGTH) yield return origFormula;
int startIdx = 0;
int endIdx = startIdx + Constants.CUT_LENGTH;
while(endIdx < origFormula.Length)
{
var substr = origFormula.Substring(startIdx, Constants.CUT_LENGTH);
if(startIdx + Constants.CUT_LENGTH < origFormula.Length)
{
substr += Constants.Separator;
}
yield return substr;
startIdx += Constants.CUT_LENGTH;
endIdx = startIdx + Constants.CUT_LENGTH;
}
if (startIdx < origFormula.Length) yield return origFormula.Substring(startIdx);
}
You can refer to this article
http://msdn.microsoft.com/en-us/library/microsoft.office.interop.excel.errorcheckingoptions_members.aspx
http://msdn.microsoft.com/en-us/library/microsoft.office.interop.excel.error.ignore(v=office.11).aspx
May b it might help :)

Incrementing an IEnumerator/IEnumerable while using yield

I am trying to yield iterate through a collection and if the collection is empty then call an increment method that will get the next set of results. When the increment says there are no more results then the yield with break;
I can not use (i think) a standard IEnumerator with MoveNext() etc as the increment method returns two different types of data.
I have tried an example below but it stops after one itteration. I am hoping there is a much easier way to do this (or at least is possible just I have a bug).
static void Main(string[] args)
{
var query = new Query();
foreach(var s in query.Q1())
{
Console.WriteLine(s);
}
foreach (var s in query.Q2())
{
Console.WriteLine(s);
}
Console.ReadLine();
}
public class Query
{
int i = 0;
bool complete;
List<string> q1 = new List<string>();
List<string> q2 = new List<string>();
public IEnumerable<string> Q1()
{
if (complete)
{
yield break;
}
if (!q1.Any() && !complete)
{
Increment();
}
if (q1.Any())
{
foreach (var s in q1)
{
yield return s;
}
}
}
public IEnumerable<string> Q2()
{
if (complete)
{
yield break;
}
if (!q2.Any() && !complete)
{
Increment();
}
if (q2.Any())
{
foreach (var s in q2)
{
yield return s;
}
}
}
void Increment()
{
if (i < 10)
{
// simulate getting two types of data back (parent and two children) from datasource
q1.Add((1 * (i + 1)).ToString());
q2.Add("A: " + (1 * (i + 1)).ToString());
q2.Add("B: " + (1 * (i + 1)).ToString());
i++;
}
else
{
complete = true;
}
}
}
result:
1
A: 1
B: 1
Any ideas on a better way of doing this or where I am going wrong?
EDIT
Here is my rough and ready fix:
public IEnumerable<string> Q1()
{
var index = 0;
if (!complete)
{
while (!complete)
{
var count = q1.Count();
if (index + 1 == count)
{
for (var x = index; index < count; index++)
{
yield return q1[index];
}
}
else
{
Increment();
}
}
}
else
{
foreach (var s in q1)
{
yield return s;
}
}
}
You are adding elements only to q2 list. Thus when you call Q1 iterator, you are exiting it after checking
if (q1.Any())
When you calling Q2 iterator, you exit it after
if (q2.Any())
{
foreach (var s in q2)
{
yield return s;
}
}
This foreach loop is executed only once and it returns only three items which where added to q2 during single Increment call in Q1 iterator.
It's not very clear what you want to achieve, but here is the way you can use loop for generating return values of iterator
public IEnumerable<string> Q2()
{
for (int i = 1; i <= 10; i++) // start from 1
{
yield return i.ToString(); // do not multiply by 1
yield return "A: " + i; // .ToString() is not necessary
yield return "B: " + i;
}
}

Categories

Resources