- Home /
How to prevent loading from freezing the game ?
So i'm saving / loading chunk for a 2d procedural game. I create the data on the first laucnh, then i want to load them when needed. I have DataChunk.cs and Datasprite.cs. I'm using unityserializer from whydoidoit.com But when i'm loading the data and instantiate the sprites the game freeze for 1 or 2 second. How do i keep everything smooth ?
public void MakeWorld(int world_size_x, int world_size_y, int chunk_size_x, int chunk_size_y)
{
world = new DataWorld(world_size_x, world_size_y);
chunks = new DataChunk[world_size_x, world_size_y];
for(int x = 0; x < world_size_x; x++)
{
for(int y = 0; y < world_size_y; y++)
{
chunks[x,y] = new DataChunk(chunk_size_x * x, chunk_size_y * y, chunk_size_x, chunk_size_y);
MakeChunk(chunks[x,y], chunk_size_x,chunk_size_y);
if(x==0 && y==0)
{
chunks[x,y].climat = GetRandomEnum<DataChunk.CLIMAT>((int) DataChunk.CLIMAT.ARID, (int) DataChunk.CLIMAT.TROPICAL+1);
}
}
}
for(int x = 0; x < world_size_x; x++)
{
for(int y = 0; y < world_size_y; y++)
{
CheckChunkPos(chunks,x,y);
chunks[x,y].UpdateClimat();
GenerateGraphics(chunks,x,y, chunks[x,y].spriteTileData);
SaveChunkData(chunks[x,y],x,y);
}
}
}
void Update()
{
CheckPlayerPosition();
}
public void CheckPlayerPosition()
{
int startX = (int)Mathf.Floor(player.position.x / chunk_size_x) -3;
int startY = (int)Mathf.Floor(player.position.y / chunk_size_y) -3;
if (startX < 0) startX = 0;
if (startY < 0) startY = 0;
//Debug.Log(startX);
int endX = (int)Mathf.Floor(player.position.x / chunk_size_x) +5;
int endY = (int)Mathf.Floor(player.position.y / chunk_size_y) +3;
if(endX>world_size_x) endX = world_size_x;
if(endY>world_size_y) endY = world_size_y;
//Debug.Log(endX);
for (int x = startX; x < endX; x++)
{
for (int y = startY; y < endY; y++)
{
if (world.chunks[x, y] != null)
break;
else
LoadChunk(x, y);
}
}
}
public void LoadChunk(int chunkX, int chunkY)
{
DataChunk chunk = LoadChunkData(chunkX,chunkY);
world.chunks[chunkX,chunkY] = chunk;
GameObject chunkGO = new GameObject();
chunkGO.name = "CHUNK" + " " + chunkX + " " + chunkY;
chunkGO.transform.position = new Vector3 (chunks[chunkX,chunkY].posX, chunks[chunkX,chunkY].posY,0);
for(int x=0; x< chunk_size_x; x++)
{
for(int y = 0; y< chunk_size_y; y++)
{
if((y+chunk.posY)<PerlinNoise((x+chunk.posX)/1,0,500,1,50,1f))
{
spriteClone = (SpriteRenderer)Instantiate(spr, new Vector2(chunk.spriteTileData[x,y].posX,chunk.spriteTileData[x,y].posY), Quaternion.identity);
spriteClone.transform.parent = chunkGO.transform;
spriteClone.name = "sprite " +x + " , " +y;
UpdateSpritesGraphics(TestChunk(chunks,chunkX,chunkY), chunk.spriteTileData[x,y], spriteClone);
}
}
}
}
string Name = "SAVED CHUNKS";
public void SaveChunkData(DataChunk chunk, int x, int y)
{
if (!Directory.Exists(Name))
Directory.CreateDirectory(Name);
FileStream fileStream = File.Create(Name + "/" + x +" , "+ y + ".ck");
StreamWriter writer = new StreamWriter(fileStream);
UnitySerializer.Serialize(chunk, fileStream);
writer.Close();
}
public DataChunk LoadChunkData(int x, int y)
{
FileStream fileStream = File.Open(Name + "/" + x +" , "+ y + ".ck",FileMode.Open);
StreamReader reader = new StreamReader(fileStream);
DataChunk chunk = UnitySerializer.Deserialize<DataChunk>(fileStream);
return chunk;
}
Answer by HappyMoo · Jan 24, 2014 at 11:03 PM
If your loading takes two seconds, don't do all the work in one frame, but space it out over multiple frames.
see this: http://docs.unity3d.com/Documentation/ScriptReference/MonoBehaviour.StartCoroutine.html
you return null to wait for the next frame. You need to experiment a little how much work you get done in one frame or measure time, because if you do too little work in one frame, the loading will take even longer.
As a good starting value I would recommend to load world_size_x*world_size_x/(2*60) chunks at once, then switch to the next frame.
This calculation is based on the idea that loading world_size_x*world_size_x chunks takes 2 seconds, so we calculate how many chunks you could load in one frame.
mhh i think i understand the idea, but in fact i'm only loading one chunk at a time for the moment(i exagerated a bit it takes less than one second to load in fact but it still freeze the game) Is that normal ? (my files are only 146kb, but unity serializer automatically encrypt them) So i'll guess i need to deserialize in more than one frame ? How would you do that ?
I would argue that your worldsaves don't really need encryption, but anyway... if LoadChunkData is the Bottleneck and not the instantiating of the objects, I would recommend to do the loading in another thread as you can't really chop the loading up as it's external code... and when ready, switch back and instantiate your GOs...
You can use the Thread package described here: http://unitygems.com/threads/
And it seems that UnitySerializer is also already using it for some things, because I've seen documentation for Loom in the UnitySerializer API reference.
Interesting article !But i'm not sure if i understood everything because what i'm doing is not working.
public void LoadChunk(int chunkX, int chunkY)
{ Debug.Log ("here");
Loom.RunAsync(()=>
{
System.Diagnostics.Stopwatch stopwatch = new System.Diagnostics.Stopwatch();
stopwatch.Start();
DataChunk chunk = LoadChunkData(chunkX,chunkY);
Debug.Log("time loading : "+stopwatch.Elapsed$$anonymous$$illiseconds);
world.chunks[chunkX,chunkY] = chunk;
GameObject chunkGO = new GameObject();
chunkGO.name = "CHUN$$anonymous$$" + " " + chunkX + " " + chunkY;
chunkGO.transform.position = new Vector3 (chunks[chunkX,chunkY].posX, chunks[chunkX,chunkY].posY,0);
stopwatch.Reset();
stopwatch.Start();
Loom.QueueOn$$anonymous$$ainThread(()=>
{
for(int x=0; x< chunk_size_x; x++)
{
for(int y = 0; y< chunk_size_y; y++)
{
if((y+chunk.posY)<PerlinNoise((x+chunk.posX)/1,0,500,1,50,1f))
{
spriteClone = (SpriteRenderer)Instantiate(spr, new Vector2(chunk.spriteTileData[x,y].posX,chunk.spriteTileData[x,y].posY), Quaternion.identity);
spriteClone.transform.parent = chunkGO.transform;
spriteClone.name = "sprite " +x + " , " +y;
UpdateSpritesGraphics(TestChunk(chunks,chunkX,chunkY), chunk.spriteTileData[x,y], spriteClone);
// spriteClone.sprite = sprites[0];
}
}
}
stopwatch.Stop();
Debug.Log("time instantiating : "+stopwatch.Elapsed$$anonymous$$illiseconds);
});
});
}
It doesn't enter the Loom. And after one try if i press the start button again, unity crashes. But it enter LoadChunk like 280 to 300 times ins$$anonymous$$d of 3.
Answer by gfoot · Jan 27, 2014 at 01:45 PM
Before you do anything, use the profiler, or your own timing code using System.Stopwatch, to measure what is taking time, then use that to inform your decisions on thing like chunk size, spreading cost between frames, etc.
Profiler is pro only, so i looked into stopwatch. I'm learning to program so it's the first time i used it. If i did it right it takes about 550 milliseconds to load a chunk and between 50 to 190 milliseconds to instatiate it depending on the chunk. But except for that i don't know how to measure anything else.
Based on that ti$$anonymous$$g data the most important thing to do is sort out LoadChunkData. The instantiation takes much less time overall, and you can look at that afterwards.
You might be able to drill down into the 500 millisecond cost of LoadChunkData by changing the function to read the file into a memory stream, then feeding the memory stream into the deserialize method. That way you can time them separately and work out which one is slow.
If file loading is slow then it's easy to amortize it, and background-loading to a memory buffer is also easy to perform on a separate thread.
If UnitySerializer is slow then maybe you can add more fine-grained ti$$anonymous$$g checks to its code, or use another serializer. If loading time (rather than blocking) is important to you, consider bespoke serialization code for your assets rather than generic serialization wrappers.
Your answer
Follow this Question
Related Questions
Sharing Violation On Path 1 Answer
Save/Load Animation State of Instantiated Prefabs 0 Answers
Saving and loading game with PlayerPrefs 1 Answer
Serialization Location 1 Answer
Serializing a graph 2 Answers