I have a very large environment which I have to load/unload in unity Webgl as I can't load them at once. So, I have divide the environment into 1000 x 1000m (1km) (each chunk is consist of three layer like objects layer, tree layer,floor layer) chunks and on my player I have attached this script for chunk loading/unloading, thanks to this guide.

    public class TileLoadingManager : MonoBehaviour
{
//[SerializeField]
public string tileTag;

//[SerializeField]
public Vector3 tileSize;

//[SerializeField]
public int maxDistance;

public GameObject[] tiles;

// Use this for initialization
void Start()
{
this.tiles = GameObject.FindGameObjectsWithTag(tileTag);
DeactivateDistantTiles();
}

void DeactivateDistantTiles()
{
Vector3 playerPosition = this.gameObject.transform.position;

foreach (GameObject tile in tiles)
{
Vector3 tilePosition = tile.gameObject.transform.position + (tileSize / 2f);

float xDistance = Mathf.Abs(tilePosition.x - playerPosition.x);
float zDistance = Mathf.Abs(tilePosition.z - playerPosition.z);

if (xDistance + zDistance > maxDistance)
{
tile.SetActive(false);

}
else
{
tile.SetActive(true);
}
}
}

void Update()
{
DeactivateDistantTiles();

}
}


Its loading/unloading my chunks but a problem is that, it is a costly method for load/unload as my object chunk (tile) count is around 4 (expected to grow). Additionally I am loading asset bundle from UnityWebReqest and destroying assets on-unload, so it very heavy task. So my question is that is this the right approach or I should need to adopt anything else?

public void AbLoadCall()
{
{
}
}

{
for (int i = 0; i < perRegionAssetBundleList.Length; i++)
{
}

}


    public IEnumerator DownloadABCall()
{
if (!abObjectInstantiated)
{
}
else
{
//Debug.Log("Reactivintg");
if (!abObjectInstantiated.activeInHierarchy)
{
//Debug.Log("Reactive done"+ this.assetName);
abObjectInstantiated.SetActive(true);
}

if (floorL7MeshRenderer)
{
floorL7MeshRenderer.enabled = false;
}

}
//else
//{
//    Debug.Log(this.name + " , Bundle object has alread available.");
//}
}

{

www = UnityWebRequestAssetBundle.GetAssetBundle(bundleURL);
//Debug.Log("web req about to send : " + assetName);

yield return www.SendWebRequest();

if (www.error != null)
{
Debug.LogError("assetBundleURL : " + assetBundleURL);
Debug.LogError("www error : " + www.error);
www.Dispose();
www = null;
yield break;
}

GameObject bundlePrefab = null;

// if we got something out
if (bundlePrefab != null)
{

abObjectInstantiated = (GameObject)Instantiate(bundlePrefab);
abObjectInstantiated.transform.parent = envParent.transform;

if (floorL7MeshRenderer)
{
floorL7MeshRenderer.enabled = false;
}

}

www.Dispose();
www = null;

bundle = null;

}

• There are some objects (buildings floor etc) which i want to load and unload based on certain distance from my player(camera). – Muhammad Faizan Khan Apr 1 at 9:09
• Can you show me what you are doing at tile.GetComponent<ABLoader>().LoadBundle();? – Saad Anees Apr 1 at 10:43
• @SaadAnees I have updated the code. – Muhammad Faizan Khan Apr 1 at 11:12

I am assuming you are loading AssetBundle at Start. If so then your method DeactivateDistantTiles() is too expensive to be called in Update() since its been called every frame and your Coroutines are nested and in a for loop and telling Update method to delay which is wrong until and unless it is being validated properly. So you need to change your code structure a little bit.

List<GameObject> listOfGameObjects = new List<GameObject>();


if (bundlePrefab != null)
{
}


Then in Update you can do something like this: (You can modify according to your need)

public void Update()
{
{
foreach (var bundle in listOfGameObjects)
{
abObjectInstantiated = (GameObject)Instantiate(bundle);
abObjectInstantiated.transform.parent = envParent.transform;

if (floorL7MeshRenderer)
{
floorL7MeshRenderer.enabled = false;
}
}
}
{
//Restrict player to go beyond your Game View screen.
}

}


Also try to use CachedAssetBundle. Its useful when you download assets frequently. According to Unity's docs

The caching system keeps track of the last version number passed to UnityWebRequest. When this API is called with a version number, the caching system checks to see if there is a cached AssetBundle by comparing version numbers. If these numbers match, the system will load the cached AssetBundle. If the numbers do not match, or there is no cached AssetBundle, then Unity will download a new copy. This new copy will be associated with the new version number.

• Sorry but your solution is not feasilbe for the problem. I can't load all assts at start i have load the assets dynamically based on camera position. additionally, Unity Built-in cache system is a crap that why i am not using it, i am using unity alternate plugin(XMLHttpRequest for it, it provide caching as well. – Muhammad Faizan Khan Apr 1 at 13:41

I would suggest that you sort these chunks into a lookup table, either a 2D array GameObject[,] or a Dictionary<int2, GameObject>. For each of your chunks you would then store it in your lookup on the index int2(floor(x / tileSize), floor(z / tileSize)).

When iterating your chunks you only have to go over a certain number of indices, rather than going over the whole list of chunks. In pseudo code this could look like:

int maxDist = ceil(maxDistance / tileSize);
int2 myPos = new int2(floor(me.x / tileSize), floor(me.z / tileSize));
for (int y = myPos.y - maxDist; y < myPos.y + maxDist; y++)
for (int x = myPos.z - maxDist; z < myPos.z + maxDist; z++)
if (chunkLookup.TryGetValue(new int2(x, y), out var chunk)
if (!chunk.active)
activate(chunk);


By storing all active chunks in a list, you can easily iterate over the active chunks, to see which ones to unload, and remove from the active list.

Edit: You might need to enable Unity.Mathematics in the Package Manager, in order to use int2.

• Thanks for your answer, can you define your code a bit more. I tried to understand and implement it but unable to grip the concept completely. – Muhammad Faizan Khan Mar 30 at 10:01
• What Jannek is describing is nearly identical to a spatial hash map. - zufallsgenerator.github.io/2014/01/26/… - zufallsgenerator.github.io/2014/01/26/… – Blue_Pyro Mar 31 at 0:44
• What you basically do is split your world into chunks then only use the chunks touching and/or near your target object. Or you can get an area and get all chunks in that area for things like rendering. It's hella handy. – Blue_Pyro Mar 31 at 0:48

The simplest thing I can think of right now would be to change it into much smaller chunks (something like Minecraft's 16x16 chunks) but maybe more like 64x64. If any chunks are within, say, 256 units of the player, then they will be loaded. Once they go outside of that, they would be unloaded, and any new chunks would be then loaded.

I am sorry, but I cannot think of any code for it as my computer with unity on it is currently being repaired.

But that should not be too hard to implement.

• but the question is how do i know that which chunk i load? – Muhammad Faizan Khan Apr 4 at 14:35
• with that, you could have a function that generates a numerical value of the chunk to be accessed (if the chunkmap would be 100 x 100, then the chunk id for the chunk at x, y on the chunk map could be 100*x+y) then you access that specific chunk through, say, a post http request to the server holding the chunk data. – Caleb Evans Apr 19 at 22:05
• But how do i will find that my camera is within range of a specific chunk? – Muhammad Faizan Khan Apr 22 at 4:25