Wayback Machinekoobas.hobune.stream
May JUN Jul
Previous capture 12 Next capture
2021 2022 2023
1 capture
12 Jun 22 - 12 Jun 22
sparklines
Close Help
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
  • Asset Store
  • Get Unity

UNITY ACCOUNT

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account
  • Blog
  • Forums
  • Answers
  • Evangelists
  • User Groups
  • Beta Program
  • Advisory Panel

Navigation

  • Home
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
    • Blog
    • Forums
    • Answers
    • Evangelists
    • User Groups
    • Beta Program
    • Advisory Panel

Unity account

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account

Language

  • Chinese
  • Spanish
  • Japanese
  • Korean
  • Portuguese
  • Ask a question
  • Spaces
    • Default
    • Help Room
    • META
    • Moderators
    • Topics
    • Questions
    • Users
    • Badges
  • Home /
avatar image
0
Question by StuwuStudio · Aug 02, 2017 at 01:31 PM · coroutinesprocedural meshworldthreading

Optimizing the generation of a procedural world - 50ms lag spike to

I've finished creating the infinite terrain generator "engin" for my game recently (The object creator and the cliff are still not implemented yet but I'll finish that later).

After optimizing it (By reducing the GC time, reducing the number of mesh to create collider on, adding safe threading & coroutine support) I still get around

50ms to create 191 new chunk (40% of that 50ms is used to create 5 new collider mesh) + 43 KB GC alloc

and about

185ms to create 1009 new chunk (generate 1009 chunk == all the chunk in a world) (22% for collider stuff) + 197.7KB GC alloc

I would like to mention that almost 100% of the time it takes to generate a chunk (and 100% of the GC alloc) come for the function named "GenerateWorldObject" (One of the only function that isn't thread safe, it has to be executed in the coroutine instead of the thread) and here's what takes to most time to load in this function

alt text

The script:

 IEnumerator LoadNewChunk () {
         bool ActionRunning = false;
 
         if(AlreadyLoading) {
             ChunkToLoad = NewChunkPos;
             yield break;
         }
 
         AlreadyLoading = true;
         Vector2 PlayerPos = ChunkToLoad;
 
         PrepareActions.Clear();
         ThreadingActions.Clear();
 
         NewChunk = new Chunk[0];
 
         Action Preparing = () => {
             NewChunkRequirement = new List<ChunkParameters>();
             //NewCRequirement0 = new List<ChunkParameters>();
             //NewCRequirement1 = new List<ChunkParameters>();
             //NewCRequirement2 = new List<ChunkParameters>();
             //NewCRequirement3 = new List<ChunkParameters>();
 
             for(int y = -Mathf.CeilToInt(LOD4EndChunkDistance); y <= Mathf.Ceil(LOD4EndChunkDistance); y++) {
                 for(int x = -Mathf.CeilToInt(LOD4EndChunkDistance); x <= Mathf.Ceil(LOD4EndChunkDistance); x++) {
                     float Distance = Vector2.Distance(Vector2.zero,new Vector2(x,y));
 
                     if(Distance <= LOD0EndChunkDistance) {
                         //NewCRequirement0.Add(new ChunkParameters(0,new Vector2(NewChunkPos.x+x,NewChunkPos.y+y),(Distance <= EndOfChunkMesh)));
                         NewChunkRequirement.Add(new ChunkParameters(0,new Vector2(NewChunkPos.x+x,NewChunkPos.y+y),(Distance <= EndOfChunkMesh)));
                     } else if(Distance <= LOD1EndChunkDistance) {
                         //NewCRequirement1.Add(new ChunkParameters(1,new Vector2(NewChunkPos.x+x,NewChunkPos.y+y),false));
                         NewChunkRequirement.Add(new ChunkParameters(1,new Vector2(NewChunkPos.x+x,NewChunkPos.y+y),false));
                     } else if(Distance <= LOD2EndChunkDistance) {
                         //NewCRequirement2.Add(new ChunkParameters(2,new Vector2(NewChunkPos.x+x,NewChunkPos.y+y),false));
                         NewChunkRequirement.Add(new ChunkParameters(2,new Vector2(NewChunkPos.x+x,NewChunkPos.y+y),false));
                     } else if(Distance <= LOD3EndChunkDistance) {
                         //NewCRequirement3.Add(new ChunkParameters(3,new Vector2(NewChunkPos.x+x,NewChunkPos.y+y),false));
                         NewChunkRequirement.Add(new ChunkParameters(3,new Vector2(NewChunkPos.x+x,NewChunkPos.y+y),false));
                     } else if(Distance <= LOD4EndChunkDistance) {
                         //NewCRequirement3.Add(new ChunkParameters(4,new Vector2(NewChunkPos.x+x,NewChunkPos.y+y),false));
                         NewChunkRequirement.Add(new ChunkParameters(4,new Vector2(NewChunkPos.x+x,NewChunkPos.y+y),false));
                     }
                 }
             }
             //NewChunkRequirement.AddRange(NewCRequirement3);
             //NewChunkRequirement.AddRange(NewCRequirement2);
             //NewChunkRequirement.AddRange(NewCRequirement1);
             //NewChunkRequirement.AddRange(NewCRequirement0);
 
             DoNotRecreate.Clear();
             NewChunk = new Chunk[NewChunkRequirement.Count];
         };
 
         PreparingThread = new Thread(new ThreadStart(Preparing));
         PreparingThread.Start();
         yield return new WaitUntil(() => !PreparingThread.IsAlive);
 
         Action Cleaning = () => {
             for(int i = 0; i < ChunkList.Length; i++) {
                 ChunkParameters cparams = new ChunkParameters(ChunkList[i].GetLODLevel(),ChunkList[i].GetChunkPosition(),ChunkList[i].IsColliderActive());
 
                 bool IsFound = false;
 
                 for(int s = 0; s < NewChunkRequirement.Count; s++) {
                     if(NewChunkRequirement[s].ChunkPos.x == cparams.ChunkPos.x && NewChunkRequirement[s].ChunkPos.y == cparams.ChunkPos.y && NewChunkRequirement[s].ColliderMesh == cparams.ColliderMesh && NewChunkRequirement[s].LevelOfDetails == cparams.LevelOfDetails) {
                         IsFound = true;
 
                         NewChunk[s] = ChunkList[i];
                         DoNotRecreate.Add(s);
                         break;
                     }
                 }
 
                 if(!IsFound) {
                     Chunk save = ChunkList[i];
 
                     Action DestroyChunkAction = () => {
                         ActionRunning = true;
                         save.DestroyChunk();
                         ActionRunning = false;
                     };
                     PrepareActions.Add(DestroyChunkAction);
                 }
             }
         };
 
         CleaningThread = new Thread(new ThreadStart(Cleaning));
         CleaningThread.Start();
         yield return new WaitUntil(() => !CleaningThread.IsAlive);
 
 
         Action Generating = () => {
             for(int i = 0; i < NewChunkRequirement.Count; i++) {
 
                 bool DoNotRecreateGate = false;
                 for(int s = 0; s < DoNotRecreate.Count; s++) {
                     if(DoNotRecreate[s] == i) {
                         DoNotRecreateGate = true;
                     }
                 }
                 if(!DoNotRecreateGate) {
                     NewChunk[i] = new Chunk(NewChunkRequirement[i].ChunkPos,Mathf.RoundToInt(Mathf.Pow(2,NewChunkRequirement[i].LevelOfDetails)),SimulatedChunkSize,worldTextures,parameters,MeshTemplate,NewChunkRequirement[i].ColliderMesh);
                     Chunk save = NewChunk[i];
 
                     Action PrepareChunk = () => {
                         save.PrepareGeneration();
                     };
                     ThreadingActions.Add(PrepareChunk);    
 
                     Action GenerateChunk = () => {
                         save.GenerateWorldObject();
 
                     };
                     PrepareActions.Add(GenerateChunk); //Instead of adding it at the end, you should put it after the creating the chunk that was loading there
                 }
             }
         };
 
         GeneratingThread = new Thread(new ThreadStart(Generating));
         GeneratingThread.Start();
         yield return new WaitUntil(() => !GeneratingThread.IsAlive);
 
         Action ExecuteThreadingActions = () => {
             while(ThreadingActions.Count > 0) {
                 Action action = ThreadingActions[ThreadingActions.Count-1];
                 ThreadingActions.RemoveAt(ThreadingActions.Count-1);
 
                 action();
             }
             ThreadingActions.Clear();
         };
 
         ActionThread = new Thread(new ThreadStart(ExecuteThreadingActions));
         ActionThread.Start();
         yield return new WaitUntil(() => !ActionThread.IsAlive);
 
         //int Counter = 0;
         while(PrepareActions.Count > 0) {
             Action action = PrepareActions[PrepareActions.Count-1];
             PrepareActions.RemoveAt(PrepareActions.Count-1);
 
             action();
 
             /*if(Counter >= 40) {
                 Counter = 0;
                 yield return new WaitForEndOfFrame();
             } else {
                 Counter++;
             }*/
         }
         PrepareActions.Clear();
 
         ChunkList = NewChunk;
         PreLoad = false;
         AlreadyLoading = false;
 
         yield return null;
     }

Some other functions:

 public class Chunk {
     Vector2 TestStartPos = new Vector2(0,0);
     public GameObject ChunkObject;
     public List<GameObject> OldObject = new List<GameObject>();
 
     WorldTexture[] worldTextures;
     WorldParameters parameters;
     int BlockSize = 1;
     int SimulatedChunkSize = 16;
     GameObject MeshTemplate;
 
     bool GenerateCollider = false;
 
     public Vector2 GetChunkPosition () {
         return TestStartPos;
     }
 
     public int GetLODLevel () {
         return Mathf.RoundToInt(Mathf.Log(BlockSize,2));
     }
 
     public bool IsColliderActive () {
         return GenerateCollider;
     }
 
     public Chunk(Vector2 Position, int blockSize, int simulatedChunkSize, WorldTexture[] textures, WorldParameters worldParameters, GameObject mesh, bool CreateCollider) {
         worldTextures = textures;
         parameters = worldParameters;
         BlockSize = blockSize;
         SimulatedChunkSize = simulatedChunkSize;
         GenerateCollider = CreateCollider;
 
         TestStartPos = Position;
         MeshTemplate = mesh;
     }
 
     public void SetNewValues (Vector2 Position, int blockSize, int simulatedChunkSize, WorldTexture[] textures, WorldParameters worldParameters, GameObject mesh) {
         worldTextures = textures;
         parameters = worldParameters;
         BlockSize = blockSize;
         SimulatedChunkSize = simulatedChunkSize;
 
         TestStartPos = Position;
         MeshTemplate = mesh;
     }
 
     public void ClearChunk () {
         OldObject.Add(ChunkObject);
         ChunkObject = null;
     }
 
     public void DestroyChunk () {
         UnityEngine.Object.Destroy(ChunkObject);
 
         foreach(GameObject chunk in OldObject) {
             UnityEngine.Object.Destroy(chunk);
         }
         OldObject = new List<GameObject>();
     }
 
     Vector2 VCode1 = new Vector2(0,0);
     Vector2 VCode2 = new Vector2(1,0);
     Vector2 VCode3 = new Vector2(0,1);
     Vector2 VCode4 = new Vector2(1,1);
 
     public bool IsPreparing = false;
 
     //Declaring for the preparation
     Vector3[] vertices = new Vector3[0];
     int[] triangles = new int[0];
     Vector2[] UVs = new Vector2[0];
 
     List<Vector3> physXverts = new List<Vector3>();
     List<int> physXtris = new List<int>();
 
     public void PrepareGeneration () {
         IsPreparing = true;
 
         int[,] xWallHeight = new int[SimulatedChunkSize/BlockSize,SimulatedChunkSize/BlockSize];
         int[,] yWallHeight = new int[SimulatedChunkSize/BlockSize,SimulatedChunkSize/BlockSize];
 
         float nY = 0f;
         float nX = 0f;
         float nZ = 0f;
 
         float xCoord = 0f;
         float yCoord = 0f;
 
         float xPos = 0f;
         float yPos = 0f;
 
         int c = 0;
         int v = 0;
         int c2 = 0;
         int neg = 0;
         int p = 0;
         int p2 = 0;
 
         p = 0;
         for(int y = 0; y < SimulatedChunkSize/BlockSize; y++) {
             for(int x = 0; x < SimulatedChunkSize/BlockSize; x++) {
                 p = ((x+y*SimulatedChunkSize/BlockSize)*4);
                 nY = GetValueAtPixel(x*BlockSize+(TestStartPos.x*(SimulatedChunkSize)),y*BlockSize+(TestStartPos.y*(SimulatedChunkSize)));
 
                 physXverts.Add(new Vector3(x,nY,y));
                 physXverts.Add(new Vector3(x+1,nY,y));
                 physXverts.Add(new Vector3(x,nY,y+1));
                 physXverts.Add(new Vector3(x+1,nY,y+1));
 
                 physXtris.Add(p+1);
                 physXtris.Add(p+0);
                 physXtris.Add(p+2);
 
                 physXtris.Add(p+1);
                 physXtris.Add(p+2);
                 physXtris.Add(p+3);
             }
         }
 
         p = 0;
         p2 = physXverts.Count;
         for(int y = 0; y < SimulatedChunkSize/BlockSize; y++) {
             for(int x = 0; x < SimulatedChunkSize/BlockSize; x++) {
                 p = ((x+y*SimulatedChunkSize/BlockSize)*4);
                 nX = GetValueAtPixel(x*BlockSize+(TestStartPos.x*SimulatedChunkSize)+BlockSize,y*BlockSize+(TestStartPos.y*SimulatedChunkSize));
                 nY = GetValueAtPixel(x*BlockSize+(TestStartPos.x*SimulatedChunkSize),y*BlockSize+(TestStartPos.y*SimulatedChunkSize));
                 nZ = GetValueAtPixel(x*BlockSize+(TestStartPos.x*SimulatedChunkSize),y*BlockSize+(TestStartPos.y*SimulatedChunkSize)+BlockSize);
 
                 physXverts.Add(new Vector3(x+1,nX,y));
                 physXverts.Add(new Vector3(x+1,nX,y+1));
 
                 physXverts.Add(new Vector3(x+1,nZ,y+1));
                 physXverts.Add(new Vector3(x,nZ,y+1));
 
                 physXtris.Add(p+3);
                 physXtris.Add(p+p2);
                 physXtris.Add(p+1);
 
                 physXtris.Add(p+p2);
                 physXtris.Add(p+p2+1);
                 physXtris.Add(p+3);
 
                 physXtris.Add(p+2);
                 physXtris.Add(p+p2+3);
                 physXtris.Add(p+3);
 
                 physXtris.Add(p+p2+3);
                 physXtris.Add(p+p2+2);
                 physXtris.Add(p+3);
             }
         }
 
 
         int Dispacement = 0;
         for(int y = 0; y < SimulatedChunkSize/BlockSize; y++) {
             for(int x = 0; x < SimulatedChunkSize/BlockSize; x++) {
                 nX = GetValueAtPixel(x*BlockSize+(TestStartPos.x*SimulatedChunkSize)+BlockSize,y*BlockSize+(TestStartPos.y*SimulatedChunkSize));
                 nY = GetValueAtPixel(x*BlockSize+(TestStartPos.x*SimulatedChunkSize),y*BlockSize+(TestStartPos.y*SimulatedChunkSize));
                 nZ = GetValueAtPixel(x*BlockSize+(TestStartPos.x*SimulatedChunkSize),y*BlockSize+(TestStartPos.y*SimulatedChunkSize)+BlockSize);
 
                 neg = 1;
                 if(nX-nY < 0) {
                     neg = -1;
                 }
 
                 if(neg == 1) {
                     Dispacement += Mathf.Abs(Mathf.FloorToInt(nY-nX));
                 } else {
                     Dispacement += Mathf.Abs(Mathf.CeilToInt(nY-nX));
                 }
 
                 neg = 1;
                 if(nZ-nY < 0) {
                     neg = -1;
                 }
 
                 if(neg == 1) {
                     Dispacement += Mathf.Abs(Mathf.FloorToInt(nY-nZ));
                 } else {
                     Dispacement += Mathf.Abs(Mathf.CeilToInt(nY-nZ));
                 }
             }
         }
 
         int[] Triangles = new int[(Dispacement + (SimulatedChunkSize/BlockSize * SimulatedChunkSize/BlockSize))*6];
         int tri = 0;
         Vector3[] Vertices = new Vector3[(Dispacement + (SimulatedChunkSize/BlockSize * SimulatedChunkSize/BlockSize))*4];
         int ver = 0;
         Vector2[] VerticesUVs = new Vector2[(Dispacement + (SimulatedChunkSize/BlockSize * SimulatedChunkSize/BlockSize))*4];
         int uv = 0;
         UVs = new Vector2[(Dispacement + (SimulatedChunkSize/BlockSize * SimulatedChunkSize/BlockSize))*6];
 
         for(int y = 0; y < SimulatedChunkSize/BlockSize; y++) {
             for(int x = 0; x < SimulatedChunkSize/BlockSize; x++) {
                 c = ((x+y*SimulatedChunkSize/BlockSize)*4);
 
                 nY = GetValueAtPixel(x*BlockSize+(TestStartPos.x*(SimulatedChunkSize)),y*BlockSize+(TestStartPos.y*(SimulatedChunkSize)));
 
                 xCoord = x*BlockSize+(TestStartPos.x*(SimulatedChunkSize));
                 yCoord = y*BlockSize+(TestStartPos.y*(SimulatedChunkSize));
 
 
                 Vertices[ver++] = (new Vector3(x,nY,y));
                 VerticesUVs[uv++] = ComplexTextureInfoToUV(GetTextureTypeAtPixel(xCoord,yCoord),0,VCode1);
                 Vertices[ver++] = (new Vector3(x+1,nY,y));
                 VerticesUVs[uv++] = ComplexTextureInfoToUV(GetTextureTypeAtPixel(xCoord,yCoord),0,VCode2);
                 Vertices[ver++] = (new Vector3(x,nY,y+1));
                 VerticesUVs[uv++] = ComplexTextureInfoToUV(GetTextureTypeAtPixel(xCoord,yCoord),0,VCode3);
                 Vertices[ver++] = (new Vector3(x+1,nY,y+1));
                 VerticesUVs[uv++] = ComplexTextureInfoToUV(GetTextureTypeAtPixel(xCoord,yCoord),0,VCode4);
 
                 Triangles[tri] = (c+1);
                 tri++;
                 Triangles[tri] = (c+0);
                 tri++;
                 Triangles[tri] = (c+2);
                 tri++;
 
                 Triangles[tri] = (c+1);
                 tri++;
                 Triangles[tri] = (c+2);
                 tri++;
                 Triangles[tri] = (c+3);
                 tri++;
             }
         }
 
         v = ver;
         c2 = v;
         for(int y = 0; y < SimulatedChunkSize/BlockSize; y++) {
             for(int x = 0; x < SimulatedChunkSize/BlockSize; x++) {
                 c = 0;
 
                 nX = GetValueAtPixel(x*BlockSize+(TestStartPos.x*SimulatedChunkSize)+BlockSize,y*BlockSize+(TestStartPos.y*SimulatedChunkSize));
                 nY = GetValueAtPixel(x*BlockSize+(TestStartPos.x*SimulatedChunkSize),y*BlockSize+(TestStartPos.y*SimulatedChunkSize));
 
                 xPos = x*BlockSize+(TestStartPos.x*SimulatedChunkSize);
                 yPos = y*BlockSize+(TestStartPos.y*SimulatedChunkSize);
 
                 neg = 1;
                 if(nX-nY < 0) {
                     neg = -1;
                 }
 
                 if(neg == 1) {
                     xWallHeight[x,y] = Mathf.FloorToInt(nY-nX);
                 } else {
                     xWallHeight[x,y] = Mathf.CeilToInt(nY-nX);
                 }
 
                 for(int i = 0; i < Mathf.Abs(xWallHeight[x,y]); i++) {
                     int TextureT = 0;
                     if(nX-nY < 0) {
                         TextureT = GetTextureTypeAtPixel(xPos,yPos);
                     } else {
                         TextureT = GetTextureTypeAtPixel(xPos+1,yPos);
                     }
 
                     int tp;
                     if(i == 0) {
                         tp = 1;
                     } else if(i == 1) {
                         tp = 2;
                     } else {
                         tp = 3;
                     }
 
                     c = ver;
 
                     if(neg == 1) {
                         Vertices[ver++] = (new Vector3(x+1,nX-i,y));
                         VerticesUVs[uv++] = ComplexTextureInfoToUV(TextureT,tp,VCode1);
                         Vertices[ver++] = (new Vector3(x+1,nX-i,y+1));
                         VerticesUVs[uv++] = ComplexTextureInfoToUV(TextureT,tp,VCode2);
                         Vertices[ver++] = (new Vector3(x+1,nX-i-1,y));
                         VerticesUVs[uv++] = ComplexTextureInfoToUV(TextureT,tp,VCode3);
                         Vertices[ver++] = (new Vector3(x+1,nX-i-1,y+1));
                         VerticesUVs[uv++] = ComplexTextureInfoToUV(TextureT,tp,VCode4);
 
                         Triangles[tri] = (c+3);
                         tri++;
                         Triangles[tri] = (c+1);
                         tri++;
                         Triangles[tri] = (c+0);
                         tri++;
 
                         Triangles[tri] = (c+2);
                         tri++;
                         Triangles[tri] = (c+3);
                         tri++;
                         Triangles[tri] = (c+0);
                         tri++;
                     } else {
                         Vertices[ver++] = (new Vector3(x+1,nY-i,y));
                         VerticesUVs[uv++] = ComplexTextureInfoToUV(TextureT,tp,VCode1);
                         Vertices[ver++] = (new Vector3(x+1,nY-i,y+1));
                         VerticesUVs[uv++] = ComplexTextureInfoToUV(TextureT,tp,VCode2);
                         Vertices[ver++] = (new Vector3(x+1,nY-i-1,y));
                         VerticesUVs[uv++] = ComplexTextureInfoToUV(TextureT,tp,VCode3);
                         Vertices[ver++] = (new Vector3(x+1,nY-i-1,y+1));
                         VerticesUVs[uv++] = ComplexTextureInfoToUV(TextureT,tp,VCode4);
 
                         Triangles[tri] = (c+0);
                         tri++;
                         Triangles[tri] = (c+1);
                         tri++;
                         Triangles[tri] = (c+3);
                         tri++;
 
                         Triangles[tri] = (c+0);
                         tri++;
                         Triangles[tri] = (c+3);
                         tri++;
                         Triangles[tri] = (c+2);
                         tri++;
                     }
                 }
 
                 c2+=yWallHeight[x,y]*4;
             }
         }
 
         v = ver;
         c2 = v;
         for(int y = 0; y < SimulatedChunkSize/BlockSize; y++) {
             for(int x = 0; x < SimulatedChunkSize/BlockSize; x++) {
                 c = 0;
 
                 nY = GetValueAtPixel(x*BlockSize+(TestStartPos.x*SimulatedChunkSize),y*BlockSize+(TestStartPos.y*SimulatedChunkSize));
                 nZ = GetValueAtPixel(x*BlockSize+(TestStartPos.x*SimulatedChunkSize),y*BlockSize+(TestStartPos.y*SimulatedChunkSize)+BlockSize);
 
                 xPos = x*BlockSize+(TestStartPos.x*SimulatedChunkSize);
                 yPos = y*BlockSize+(TestStartPos.y*SimulatedChunkSize);
 
                 neg = 1;
                 if(nZ-nY < 0) {
                     neg = -1;
                 }
 
                 if(neg == 1) {
                     yWallHeight[x,y] = Mathf.FloorToInt(nY-nZ);
                 } else {
                     yWallHeight[x,y] = Mathf.CeilToInt(nY-nZ);
                 }
 
                 for(int i = 0; i < Mathf.Abs(yWallHeight[x,y]); i++) {
                     int TextureT = 0;
                     if(nZ-nY < 0) {
                         TextureT = GetTextureTypeAtPixel(xPos,yPos);
                     } else {
                         TextureT = GetTextureTypeAtPixel(xPos,yPos+1);
                     }
 
                     int tp;
                     if(i == 0) {
                         tp = 1;
                     } else if(i == 1) {
                         tp = 2;
                     } else {
                         tp = 3;
                     }
 
                     c = ver;
 
                     if(neg == 1) {
                         Vertices[ver++] = (new Vector3(x,nZ-i,y+1));
                         VerticesUVs[uv++] = ComplexTextureInfoToUV(TextureT,tp,VCode1);
                         Vertices[ver++] = (new Vector3(x+1,nZ-i,y+1));
                         VerticesUVs[uv++] = ComplexTextureInfoToUV(TextureT,tp,VCode2);
                         Vertices[ver++] = (new Vector3(x,nZ-i-1,y+1));
                         VerticesUVs[uv++] = ComplexTextureInfoToUV(TextureT,tp,VCode3);
                         Vertices[ver++] = (new Vector3(x+1,nZ-i-1,y+1));
                         VerticesUVs[uv++] = ComplexTextureInfoToUV(TextureT,tp,VCode4);
 
                         Triangles[tri] = (c+0);
                         tri++;
                         Triangles[tri] = (c+1);
                         tri++;
                         Triangles[tri] = (c+3);
                         tri++;
 
                         Triangles[tri] = (c+0);
                         tri++;
                         Triangles[tri] = (c+3);
                         tri++;
                         Triangles[tri] = (c+2);
                         tri++;
                     } else {
                         Vertices[ver++] = (new Vector3(x,nY-i,y+1));
                         VerticesUVs[uv++] = ComplexTextureInfoToUV(TextureT,tp,VCode1);
                         Vertices[ver++] = (new Vector3(x+1,nY-i,y+1));
                         VerticesUVs[uv++] = ComplexTextureInfoToUV(TextureT,tp,VCode2);
                         Vertices[ver++] = (new Vector3(x,nY-i-1,y+1));
                         VerticesUVs[uv++] = ComplexTextureInfoToUV(TextureT,tp,VCode3);
                         Vertices[ver++] = (new Vector3(x+1,nY-i-1,y+1));
                         VerticesUVs[uv++] = ComplexTextureInfoToUV(TextureT,tp,VCode4);
 
                         Triangles[tri] = (c+3);
                         tri++;
                         Triangles[tri] = (c+1);
                         tri++;
                         Triangles[tri] = (c+0);
                         tri++;
 
                         Triangles[tri] = (c+2);
                         tri++;
                         Triangles[tri] = (c+3);
                         tri++;
                         Triangles[tri] = (c+0);
                         tri++;
                     }
 
                 }
 
                 c2+=yWallHeight[x,y]*4;
             }
         }
 
         Vector3[] oldVerts = Vertices;
         triangles = Triangles;
         vertices = new Vector3[triangles.Length];
 
         for(int i = 0; i < triangles.Length; i++) {
             vertices[i] = oldVerts[triangles[i]];
             UVs[i] = VerticesUVs[triangles[i]];
             triangles[i] = i;
         }
 
         IsPreparing = false;
     }
 
     public void GenerateWorldObject () {
         GameObject cChunk = (GameObject)GameObject.Instantiate(MeshTemplate,new Vector3(TestStartPos.x*SimulatedChunkSize,0,TestStartPos.y*SimulatedChunkSize),Quaternion.identity);
         cChunk.transform.localScale = new Vector3(BlockSize,BlockSize,BlockSize);
 
         Mesh cMesh = new Mesh();
         cMesh.vertices = vertices;
         cMesh.triangles = triangles;
         cMesh.uv = UVs;
 
         Mesh pMesh = new Mesh();
         pMesh.SetVertices(physXverts);
         pMesh.SetTriangles(physXtris,0);
 
         cMesh.RecalculateNormals();
         pMesh.RecalculateBounds();
 
         cChunk.GetComponent<MeshFilter>().mesh = cMesh;
         if(GenerateCollider) {
             cChunk.GetComponent<MeshCollider>().sharedMesh = pMesh;
         }
 
         ChunkObject = cChunk;
     }
 
     //Texture Placement Rules:
     // 0 : TOP
     // 1 : UpperWall
     // 2 : LowerWall
     // 3 : UnderWall
 
     Vector2 Results = Vector2.zero;
 
     Vector2 ComplexTextureInfoToUV (int TextureType, int texturePlacement, Vector2 verticesUV) {
         switch(texturePlacement) {
         case 0:
             if(verticesUV.x == 0) {
                 Results.x = worldTextures[TextureType].TopCoords.x;
             } else {
                 Results.x = worldTextures[TextureType].TopCoords.z;
             }
             if(verticesUV.y == 0) {
                 Results.y = worldTextures[TextureType].TopCoords.y;
             } else {
                 Results.y = worldTextures[TextureType].TopCoords.w;
             }
             break;
         case 1:
             if(verticesUV.x == 0) {
                 Results.x = worldTextures[TextureType].UpperWallCoords.x;
             } else {
                 Results.x = worldTextures[TextureType].UpperWallCoords.z;
             }
             if(verticesUV.y == 0) {
                 Results.y = worldTextures[TextureType].UpperWallCoords.y;
             } else {
                 Results.y = worldTextures[TextureType].UpperWallCoords.w;
             }
             break;
         case 2:
             if(verticesUV.x == 0) {
                 Results.x = worldTextures[TextureType].LowerWallCoords.x;
             } else {
                 Results.x = worldTextures[TextureType].LowerWallCoords.z;
             }
             if(verticesUV.y == 0) {
                 Results.y = worldTextures[TextureType].LowerWallCoords.y;
             } else {
                 Results.y = worldTextures[TextureType].LowerWallCoords.w;
             }
             break;
         case 3:
             if(verticesUV.x == 0) {
                 Results.x = worldTextures[TextureType].UnderWallCoords.x;
             } else {
                 Results.x = worldTextures[TextureType].UnderWallCoords.z;
             }
             if(verticesUV.y == 0) {
                 Results.y = worldTextures[TextureType].UnderWallCoords.y;
             } else {
                 Results.y = worldTextures[TextureType].UnderWallCoords.w;
             }
             break;
         }
 
         return V2ToUV(Results);
     }
 
     int GetTextureTypeAtPixel (float x, float y) {
         return 0;
     }
 
     float GetValueAtPixel (float x, float y) {
         return Mathf.PerlinNoise(x*parameters.Frequency,y*parameters.Frequency)*parameters.Amplitude/(BlockSize);
     }
 
     Vector2 vcalcule = Vector2.zero;
 
     Vector2 PixelToUV (int x, int y) {
         vcalcule.x = 0;
         vcalcule.y = 0;
 
         vcalcule.x = (float)x*0.001953125f; //0.001953125 = 1/512, 512 = size of the texture
         vcalcule.y = 1-((float)y*0.001953125f); //0.001953125 = 1/512, 512 = size of the texture
         return vcalcule;
     }
 
     Vector2 V2ToUV (Vector2 v) {
         vcalcule.x = 0;
         vcalcule.y = 0;
 
         vcalcule.x = (float)v.x*0.001953125f; //0.001953125 = 1/512, 512 = size of the texture
         vcalcule.y = 1-((float)v.y*0.001953125f); //0.001953125 = 1/512, 512 = size of the texture
         return vcalcule;
     }
 
     bool IsInBound (int v, int bSize, int index) {
         return (v-(Mathf.Floor((float)v/bSize)*4))==index;
     }
 }

PS: Sorry of the headache after reading the script

capture-decran-2017-08-02-a-092516.png (24.6 kB)
Comment
Add comment · Show 2
10 |3000 characters needed characters left characters exceeded
â–¼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image ShadyProductions · Aug 02, 2017 at 01:36 PM 0
Share

$$anonymous$$ake the chunks bigger = less chunks generated = less gc. Also don't use colliders, do collision detect yourself. It helps performance a load aswel.

Also, the general aspect of infinite terrain is that you generate your surrounding environment + maybe some extra off camera, and when the player moves outside of the generated environment that you start the generate a bit more, and cache the part you left and turn it inactive. Generating it all at once, I wouldn't recommend it.

avatar image Bunny83 ShadyProductions · Aug 02, 2017 at 02:55 PM 0
Share

I wouldn't recommend to increase the chunk size. A size of 16 is already quite large. A worst case chunk section of 16x16x16x24 (==98k) is already too large for a single mesh. Of course that's only possible if you allow transparent blocks. Without transparent blocks the absolute worst case for a 16x16x16 section is 16x16x16x24 / 2 == ~50k vertices. This is basically a 3d checkerboard pattern. So every second cube is fully visible. If you plan to allow more complex block models it's getting even worse. In Unity a smaller section size would be recommended.

$$anonymous$$inecraft splits the world horizontally into regions of 32x32 chunks. A chunk is 16x16 and splitted further into 16 chunk sections vertically (build-height 256 (16x16)). However $$anonymous$$inecraft uses 32 bit index buffers while Unity currently only supports 16 bit and therefore a single mesh can't have more than 64k vertices (65536).

1 Reply

· Add your reply
  • Sort: 
avatar image
1
Best Answer

Answer by Bunny83 · Aug 02, 2017 at 02:42 PM

Actually creating 1000 gameobjects in 185ms is pretty fast. What you have to avoid is destroying and recreating the chunks all the time. You need to use an object pool for the chunk objects.

Like @ShadyProductions said in the comment you shouldn't really use colliders. I've seen some implementations which uses a pool of a few box colliders and only generate / place them around objects which could collider with them. So there would be only colliders around the player. However this requires a lot of preprocessing and bookkeeping so it's usually easier to roll your own collsion detection solution.

Comment
Add comment · Show 1 · Share
10 |3000 characters needed characters left characters exceeded
â–¼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image StuwuStudio · Aug 02, 2017 at 11:43 PM 0
Share

"Actually creating 1000 gameobjects in 185ms is pretty fast."

I'm starting to wonder if my 2013 11' $$anonymous$$acBook Air is good or my optimization skillz are good. $$anonymous$$aybe both?

"You need to use an object pool for the chunk objects."

It looks complicated on the wiki but Sebastian Lague made a video on that subject.

"Like @ShadyProductions said in the comment you shouldn't really use colliders. I've seen some implementations which use a pool of a few box colliders and only generate/place them around objects which could collide with them."

Good Idea but the problem is if I have other non-static entity, they need their own ground collider too and it'll be hard to code.

Anyway, thanks for your answers! It helped me a lot.

Your answer

Hint: You can notify a user about this post by typing @username

Up to 2 attachments (including images) can be used with a maximum of 524.3 kB each and 1.0 MB total.

Follow this Question

Answers Answers and Comments

69 People are following this question.

avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image

Related Questions

Setting SplatPrototypes is very slow! (50 ms every Terrain), coroutines/threading? (I need suggestions) 0 Answers

Lock class/struct to a single thread/worker 1 Answer

Return value from coroutine to non monobehaviour 1 Answer

Unity3D and C# - Coroutines vs threading 4 Answers

What's the best way to structure a recursive long running algorithm? 1 Answer


Enterprise
Social Q&A

Social
Subscribe on YouTube social-youtube Follow on LinkedIn social-linkedin Follow on Twitter social-twitter Follow on Facebook social-facebook Follow on Instagram social-instagram

Footer

  • Purchase
    • Products
    • Subscription
    • Asset Store
    • Unity Gear
    • Resellers
  • Education
    • Students
    • Educators
    • Certification
    • Learn
    • Center of Excellence
  • Download
    • Unity
    • Beta Program
  • Unity Labs
    • Labs
    • Publications
  • Resources
    • Learn platform
    • Community
    • Documentation
    • Unity QA
    • FAQ
    • Services Status
    • Connect
  • About Unity
    • About Us
    • Blog
    • Events
    • Careers
    • Contact
    • Press
    • Partners
    • Affiliates
    • Security
Copyright © 2020 Unity Technologies
  • Legal
  • Privacy Policy
  • Cookies
  • Do Not Sell My Personal Information
  • Cookies Settings
"Unity", Unity logos, and other Unity trademarks are trademarks or registered trademarks of Unity Technologies or its affiliates in the U.S. and elsewhere (more info here). Other names or brands are trademarks of their respective owners.
  • Anonymous
  • Sign in
  • Create
  • Ask a question
  • Spaces
  • Default
  • Help Room
  • META
  • Moderators
  • Explore
  • Topics
  • Questions
  • Users
  • Badges