Hey guy,
I’m having trouble with prefab order in a parental container. Being given a list having ids of the 3d models that we have posted them into database, now, I’m using the id to load them into our game.
But the problem I’m having is that some of them will have a different loading and cloning time, so the order of these prefabs in the container is wrong.
using System.Collections;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.Networking;
using Unity.VisualScripting;
public class LoadModelManager:MonoBehaviour {
static public IEnumerator LoadModel( int? id, GameObject self, GameObject parent = null, bool active = false, int indexOrder=0)
{
if (id == null)
{
id = 0; // skin default
}
string url = $"{GlobalVariable.server_url}/3ds/download/{id}"; // Replace with your server and image id
using (UnityWebRequest webRequest = UnityWebRequestAssetBundle.GetAssetBundle(url))
{
yield return webRequest.SendWebRequest();
if (webRequest.result != UnityWebRequest.Result.Success)
{
Debug.Log(webRequest.error);
}
else
{
AssetBundle remoteAssetBundle = DownloadHandlerAssetBundle.GetContent(webRequest);
if (remoteAssetBundle == null){
yield break;
}
Debug.Log(remoteAssetBundle.LoadAsset(remoteAssetBundle.GetAllAssetNames()[0]));
var model = Instantiate(remoteAssetBundle.LoadAsset(remoteAssetBundle.GetAllAssetNames()[0])) as GameObject;
model.transform.position = self.transform.position;
model.transform.rotation = self.transform.rotation;
model.transform.localScale = self.transform.localScale;
if (parent !=null){
model.transform.SetParent(parent.transform,false);
}
if (indexOrder!=0){
model.transform.SetSiblingIndex(indexOrder);
}
if (active == true){
model.SetActive(true);
}
else {
model.SetActive(false);
}
remoteAssetBundle.Unload(false);
}
}
}
}
I have tried to use SetSiblingIndex but seems like it still gives wrong order.
If anyone has solved this problem, please let me know how to solve this. I will be really appreciated.
Thank you. Have a nice day!
You have cut this snippet out of its broader context.
This whole approach is smelly (coroutines + async), but the caller of this method is the most likely cause of your issues.
Where is webRequest defined, and how is LoadModel called?
Edit:
An easy solution would be to reorder everything once all web requests are finished (or to sort after each completed web request, according to some known indice or values).
async void Start()
{
userBUS = new UserBUS();
var response = await userBUS.GetProfile();
if (response.isSuccessful && response.data != null)
{
personal = response.data;
skins = personal.skinsPurchased;
//skin
for (int i = 0; i < skins.Length; i++)
{
GameObject newProduct = Instantiate(prefabProduct);
var imageProduct = newProduct.transform.GetChild(0).GetComponent<Image>();
StartCoroutine(LoadImageManager.LoadBinaryImage(imageProduct, skins.imageSkinId));
// }
int index = i;
newProduct.transform.SetParent(skinTab.transform, false);
if (personal.imageSkinId == skins.imageSkinId){
newProduct.GetComponent<Image>().color = new Color32(209,165,165,255);
StartCoroutine(LoadModelManager.LoadModel(skins.threeDimensionId,prototype,container,true,i));
}
else {
StartCoroutine(LoadModelManager.LoadModel(skins.threeDimensionId,prototype,container));
}
newProduct.GetComponent<Button>().onClick.AddListener(() => CustomAvatar(index,"skin", skinTab));
}
}
This script is attached to a scene, so every time this scene is loaded, I let them called for information. This is the object data we will get. Because there are only inds in the data so I need to make another step to get the real images or 3dmodels. That’s why LoadModelManager and LoadImageManager existed. Im feeling like I should wait for each one done its order so we can continue to the next one.
The problem, as I expected, is that you don’t respect the web request, you just plow through it synchronously in that for loop. Yet, web request operates asynchronously, meaning it will get done when it wants to, not when you call it again.
You either open as many web requests and populate their results, or use a single instance of it, and wait for it sequentially. Or you can re-sort after everything settles down as suggested before.
Edit:
It’s obvious you’re not used to it, but getting things out of order is the first newbie error when working with asynchronous programming.
In general, the whole setup isn’t something I’d call a good practice.
I’d advise you to build a dedicated class that abstracts the web request usage pattern without coroutines (you don’t need coroutines at all), and encapsulates the actual work, but can signal its status and progress to the outside world, via callbacks or events.
Glancing at the code I think the other issue is using SetSiblingIndex for this.
Remember, each subsequent call to that could reorder anything already in the Transform child list.
Just make an empty array of the correct size, and load all your stuff into that array, using the same index
When all have completed, move them into place in the Transform in that order.
If you want them to populate as they come in off the wire, make sure you add blank Transform children placeholders… perhaps you already did that, I’m not sure.