I’m making a 2D process land utilizing many tile collaborators. To do that infinite (and extra performed), dynamically fragments and discharge based on the place of the gamers and a variable renderDistance
.
Because the fragments are manufactured from particular person squares, lots of/1000’s of recreation objections in a single operate should be created, this causes quite a lot of delay as the principle recreation/thread of the principle recreation stops. Initially I believed it will be a easy resolution, it might solely async
The capabilities, that approach they run together with the principle thread and never cease something. However after doing so, not one of the items generates. After slightly purification, I discovered that it appeared to cease within the creation of Gameobject:
personal async Job LoadChunkAsync(int x)
{
if (!m_chunks.TryGetValue(x, out Chunk chunk))
{
// v HERE v
GameObject chunkObject = new($"Chunk: {x}");
chunkObject.remodel.SetParent(m_chunkPool.remodel);
chunkObject.remodel.place = new Vector3(x * Chunk.CHUNK_WIDTH - Chunk.CHUNK_WIDTH / 2, 0);
chunk = chunkObject.AddComponent();
chunk.chunkX = x;
}
Terrain terrain = await m_terrainGenerator.GenerateTerrainAsync(x * Chunk.CHUNK_WIDTH - Chunk.CHUNK_WIDTH / 2, Chunk.CHUNK_WIDTH, Chunk.CHUNK_HEIGHT);
await chunk.LoadAsync(terrain.GetTiles());
m_chunks.Add(x, chunk);
}
I’m not precisely certain why this occurs, however I assumed that it needed to do with the unit was unsure of thread. To keep away from this downside, I did a unitymaintreaddispatcher class:
utilizing System.Collections.Generic;
utilizing System;
utilizing UnityEngine;
utilizing System.Threading.Duties;
public class UnityMainThreadDispatcher : MonoBehaviour
{
public static UnityMainThreadDispatcher Occasion { get; personal set; }
personal Queue m_pending = new();
personal void Awake()
{
if (Occasion != null)
{
Debug.Log("Greater than 1 UnityThreadDispatcher exists in Scene.");
Destroy(gameObject);
return;
}
Occasion = this;
DontDestroyOnLoad(gameObject);
}
void Replace()
{
lock (m_pending)
{
whereas (m_pending.Depend != 0) m_pending.Dequeue().Invoke();
}
}
public void Invoke(Motion a)
{
lock (m_pending) { m_pending.Enqueue(a); }
}
public Job InvokeAsync(Func func)
{
var tcs = new TaskCompletionSource();
Invoke(() =>
{
strive
{
tcs.SetResult(func());
}
catch (Exception e)
{
tcs.SetException(e);
}
});
return tcs.Job;
}
}
This labored, for a chunk. Nor did it work for mosaics contained in the fragment. I’ve been on this for some time and not using a resolution, it’s troublesome to purify as I’m, and I’m not so good with the capabilities of asynchronous to begin, so I hope somebody might help.
Right here is my chunkmanager and chunk class:
utilizing System;
utilizing System.Collections.Generic;
utilizing System.Linq;
utilizing System.Threading.Duties;
utilizing UnityEngine;
public class ChunkManager
{
personal Dictionary m_chunks;
personal GameManager m_gameManager;
personal TerrainGenerator m_terrainGenerator;
personal UnityMainThreadDispatcher m_mainThreadDispatcher;
personal ObjectPool m_chunkPool;
personal Queue m_loadQueue;
personal Queue m_unloadQueue;
personal bool m_isProcessingLoadQueue;
personal bool m_isProcessingUnloadQueue;
public ChunkManager(int seed)
{
m_chunks = new();
m_gameManager = GameManager.Occasion;
m_terrainGenerator = GameManager.Occasion.terrainGenerator;
m_mainThreadDispatcher = UnityMainThreadDispatcher.Occasion;
m_chunkPool = PoolManager.GetPool("Chunks");
m_loadQueue = new Queue();
m_unloadQueue = new Queue();
m_isProcessingLoadQueue = false;
m_isProcessingUnloadQueue = false;
}
public void UpdateSeed(int seed)
{
Job.Run(() => UpdateSeedAsync(seed));
}
public void ClearAllChunks()
{
Job.Run(() => ClearAllChunksAsync());
}
public void LoadChunksAroundPlayer(float playerX)
{
Job.Run(() => LoadChunksAroundPlayerAsync(playerX));
}
public void UnloadDistantChunks(float playerX)
{
Job.Run(() => UnloadDistantChunksAsync(playerX));
}
public async Job UpdateSeedAsync(int seed)
{
m_terrainGenerator.SetSeed(seed);
Checklist unloadTasks = new();
foreach (var chunk in m_chunks.Values)
{
unloadTasks.Add(UnloadChunkAsync(chunk.chunkX));
}
await Job.WhenAll(unloadTasks);
m_chunks.Clear();
}
public async Job ClearAllChunksAsync()
{
Checklist unloadTasks = new();
foreach (var chunk in m_chunks.Values)
{
unloadTasks.Add(UnloadChunkAsync(chunk.chunkX));
}
await Job.WhenAll(unloadTasks);
}
public WorldTile GetWorldTile(float x, float y)
{
Chunk chunk = GetChunkByX(x + 0.5f);
if (chunk == null)
{
return null;
}
int tileX = (Mathf.FloorToInt(x + 0.5f) - Chunk.CHUNK_WIDTH / 2) % Chunk.CHUNK_WIDTH;
if (tileX Mathf.Abs(chunk.Key - playerChunkX) > renderDistance).ToList())
{
m_unloadQueue.Enqueue(chunk.Key);
}
if (!m_isProcessingUnloadQueue)
{
await ProcessUnloadQueueAsync();
}
}
personal async Job ProcessLoadQueueAsync()
{
m_isProcessingLoadQueue = true;
whereas (m_loadQueue.Depend > 0)
{
int chunkX = m_loadQueue.Dequeue();
await LoadChunkAsync(chunkX);
}
m_isProcessingLoadQueue = false;
}
personal async Job ProcessUnloadQueueAsync()
{
m_isProcessingUnloadQueue = true;
whereas (m_unloadQueue.Depend > 0)
{
int chunkX = m_unloadQueue.Dequeue();
await UnloadChunkAsync(chunkX);
}
m_isProcessingUnloadQueue = false;
}
personal async Job LoadChunkAsync(int x)
{
if (!m_chunks.TryGetValue(x, out Chunk chunk))
{
GameObject chunkObject = await m_mainThreadDispatcher.InvokeAsync(() =>
{
GameObject obj = new($"Chunk: {x}");
obj.remodel.SetParent(m_chunkPool.remodel);
obj.remodel.place = new Vector3(x * Chunk.CHUNK_WIDTH - Chunk.CHUNK_WIDTH / 2, 0);
return obj;
});
chunk = await m_mainThreadDispatcher.InvokeAsync(() =>
{
Chunk newChunk = chunkObject.AddComponent();
newChunk.chunkX = x;
return newChunk;
});
}
Terrain terrain = await m_terrainGenerator.GenerateTerrainAsync(x * Chunk.CHUNK_WIDTH - Chunk.CHUNK_WIDTH / 2, Chunk.CHUNK_WIDTH, Chunk.CHUNK_HEIGHT);
await chunk.LoadAsync(terrain.GetTiles());
m_chunks.Add(x, chunk);
}
personal async Job UnloadChunkAsync(int chunkId)
{
if (m_chunks.TryGetValue(chunkId, out Chunk chunk))
{
await Job.Run(() => GameObject.Destroy(chunk.gameObject));
m_chunks.Take away(chunkId);
}
}
personal Chunk GetChunkByX(float x)
{
int chunkX = Mathf.RoundToInt(x / Chunk.CHUNK_WIDTH);
if (m_chunks.TryGetValue(chunkX, out Chunk chunk))
{
return chunk;
}
return null;
}
}
utilizing System.Threading.Duties;
utilizing UnityEngine;
(RequireComponent(typeof(Rigidbody2D)))
public class Chunk : MonoBehaviour
{
public const int CHUNK_WIDTH = 16;
public const int CHUNK_HEIGHT = 256;
public int chunkX;
personal UnityMainThreadDispatcher m_mainThreadDispatcher;
personal WorldTile(,) m_worldTiles;
personal Tile(,) m_tiles;
personal void Awake()
{
Rigidbody2D rb = gameObject.GetComponent();
rb.bodyType = RigidbodyType2D.Static;
CompositeCollider2D compositeCollider = gameObject.AddComponent();
compositeCollider.geometryType = CompositeCollider2D.GeometryType.Polygons;
}
personal void Begin()
{
m_mainThreadDispatcher = UnityMainThreadDispatcher.Occasion;
}
public async Job LoadAsync(Tile(,) tiles)
{
await Job.Run(async () =>
{
m_tiles = tiles;
m_worldTiles = new WorldTile(CHUNK_WIDTH, CHUNK_HEIGHT);
for (int x = 0; x
{
GameObject obj = new GameObject($"Tile_{x}_{y}");
obj.remodel.SetParent(remodel);
obj.remodel.localPosition = new Vector3(x, y);
return obj;
});
m_mainThreadDispatcher.Invoke(() =>
{
SpriteRenderer spriteRenderer = tileObject.AddComponent();
spriteRenderer.sprite = tile.sprite;
WorldTile worldTile = tileObject.AddComponent();
worldTile.tile = tile;
m_worldTiles(x, y) = worldTile;
PolygonCollider2D polygonCollider = tileObject.AddComponent();
polygonCollider.compositeOperation = Collider2D.CompositeOperation.Merge;
});
}
}
}
});
}
public void SetTile(int x, int y, Tile tile)
{
m_tiles(x, y) = tile;
}
public Tile GetTile(int x, int y)
{
return m_tiles(x, y);
}
public WorldTile GetWorldTile(int x, int y)
}
I’ve additionally examined different strategies as follows with out outcomes with which I’m blissful:
- CORUTINAS: Belief an excessive amount of on the principle recreation loop (I ideally need this to be fully unbiased, so it doesn’t trigger delay).
- Object group: it will not work on this case because the world technology is infinite and I can’t group an infinite variety of mosaics.
Edit: As Olivier stated, you may really group the mosaics. Since solely a sure quantity is loaded on the identical time. Nevertheless, it is a nice possibility, whereas grouping mosaics saves the creation of Gameobject, it’s nonetheless tremendous gradual. Which implies that it’s in all probability not the creation of Gameobject, however, in truth, the motion/repairing, and so forth., inflicting the delay.
I’ll strive the Tilemap sooner or later, nonetheless, it’s a pretty lengthy job since I should modify my current code. I’m presently solely utilizing a choroutine that’s left behind, however for an extended time, so it’s not an extended stuttering (that is the perfect resolution I’ve discovered)
Any assist could be appreciated.