1
0

EndlessTerrain.cs 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250
  1. using UnityEngine;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. public class EndlessTerrain : MonoBehaviour
  5. {
  6. const float viewerMoveThresholdForChunkUpdate = 25f;
  7. const float sqrViewerMoveThresholdForChunkUpdate = viewerMoveThresholdForChunkUpdate * viewerMoveThresholdForChunkUpdate;
  8. public LODInfo[] detailLevels;
  9. public static float maxViewDst;
  10. public Transform viewer;
  11. public Material mapMaterial;
  12. public static Vector2 viewerPosition;
  13. Vector2 viewerPositionOld;
  14. static MapGenerator mapGenerator;
  15. int chunkSize;
  16. int chunksVisibleInViewDst;
  17. Dictionary<Vector2, TerrainChunk> terrainChunkDictionary = new Dictionary<Vector2, TerrainChunk>();
  18. static List<TerrainChunk> terrainChunksVisibleLastUpdate = new List<TerrainChunk>();
  19. void Start()
  20. {
  21. mapGenerator = FindFirstObjectByType<MapGenerator>();
  22. maxViewDst = detailLevels[detailLevels.Length - 1].visibleDstThreshold;
  23. chunkSize = mapGenerator.mapChunkSize - 1;
  24. chunksVisibleInViewDst = Mathf.RoundToInt(maxViewDst / chunkSize);
  25. UpdateVisibleChunks();
  26. }
  27. void Update()
  28. {
  29. viewerPosition = new Vector2(viewer.position.x, viewer.position.z) / mapGenerator.terrainData.uniformScale;
  30. if ((viewerPositionOld - viewerPosition).sqrMagnitude > sqrViewerMoveThresholdForChunkUpdate)
  31. {
  32. viewerPositionOld = viewerPosition;
  33. UpdateVisibleChunks();
  34. }
  35. }
  36. void UpdateVisibleChunks()
  37. {
  38. for (int i = 0; i < terrainChunksVisibleLastUpdate.Count; i++)
  39. {
  40. terrainChunksVisibleLastUpdate[i].SetVisible(false);
  41. }
  42. terrainChunksVisibleLastUpdate.Clear();
  43. int currentChunkCoordX = Mathf.RoundToInt(viewerPosition.x / chunkSize);
  44. int currentChunkCoordY = Mathf.RoundToInt(viewerPosition.y / chunkSize);
  45. for (int yOffset = -chunksVisibleInViewDst; yOffset <= chunksVisibleInViewDst; yOffset++)
  46. {
  47. for (int xOffset = -chunksVisibleInViewDst; xOffset <= chunksVisibleInViewDst; xOffset++)
  48. {
  49. Vector2 viewedChunkCoord = new Vector2(currentChunkCoordX + xOffset, currentChunkCoordY + yOffset);
  50. if (terrainChunkDictionary.ContainsKey(viewedChunkCoord))
  51. {
  52. terrainChunkDictionary[viewedChunkCoord].UpdateTerrainChunk();
  53. }
  54. else
  55. {
  56. terrainChunkDictionary.Add(viewedChunkCoord, new TerrainChunk(viewedChunkCoord, chunkSize, detailLevels, transform, mapMaterial));
  57. }
  58. }
  59. }
  60. }
  61. public class TerrainChunk
  62. {
  63. GameObject meshObject;
  64. Vector2 position;
  65. Bounds bounds;
  66. MeshRenderer meshRenderer;
  67. MeshFilter meshFilter;
  68. MeshCollider meshCollider;
  69. LODInfo[] detailLevels;
  70. LODMesh[] lodMeshes;
  71. LODMesh collisionLODMesh;
  72. MapData mapData;
  73. bool mapDataReceived;
  74. int previousLODIndex = -1;
  75. public TerrainChunk(Vector2 coord, int size, LODInfo[] detailLevels, Transform parent, Material material)
  76. {
  77. this.detailLevels = detailLevels;
  78. position = coord * size;
  79. bounds = new Bounds(position, Vector2.one * size);
  80. Vector3 positionV3 = new Vector3(position.x, 0, position.y);
  81. meshObject = new GameObject("Terrain Chunk");
  82. meshObject.layer = 6; // Terrain layer
  83. meshRenderer = meshObject.AddComponent<MeshRenderer>();
  84. meshFilter = meshObject.AddComponent<MeshFilter>();
  85. meshCollider = meshObject.AddComponent<MeshCollider>();
  86. meshRenderer.material = material;
  87. meshObject.transform.position = positionV3 * mapGenerator.terrainData.uniformScale;
  88. meshObject.transform.parent = parent;
  89. meshObject.transform.localScale = Vector3.one * mapGenerator.terrainData.uniformScale;
  90. SetVisible(false);
  91. lodMeshes = new LODMesh[detailLevels.Length];
  92. for (int i = 0; i < detailLevels.Length; i++)
  93. {
  94. lodMeshes[i] = new LODMesh(detailLevels[i].lod, UpdateTerrainChunk);
  95. if (detailLevels[i].useForCollider)
  96. {
  97. collisionLODMesh = lodMeshes[i];
  98. }
  99. }
  100. mapGenerator.RequestMapData(position, OnMapDataReceived);
  101. }
  102. void OnMapDataReceived(MapData mapData)
  103. {
  104. this.mapData = mapData;
  105. mapDataReceived = true;
  106. UpdateTerrainChunk();
  107. }
  108. public void UpdateTerrainChunk()
  109. {
  110. if (mapDataReceived)
  111. {
  112. float viewerDstFromNearestEdge = Mathf.Sqrt(bounds.SqrDistance(viewerPosition));
  113. bool visible = viewerDstFromNearestEdge <= maxViewDst;
  114. if (visible)
  115. {
  116. int lodIndex = 0;
  117. for (int i = 0; i < detailLevels.Length - 1; i++)
  118. {
  119. if (viewerDstFromNearestEdge > detailLevels[i].visibleDstThreshold)
  120. {
  121. lodIndex = i + 1;
  122. }
  123. else
  124. {
  125. break;
  126. }
  127. }
  128. if (lodIndex != previousLODIndex)
  129. {
  130. LODMesh lodMesh = lodMeshes[lodIndex];
  131. if (lodMesh.hasMesh)
  132. {
  133. previousLODIndex = lodIndex;
  134. meshFilter.mesh = lodMesh.mesh;
  135. }
  136. else if (!lodMesh.hasRequestedMesh)
  137. {
  138. lodMesh.RequestMesh(mapData);
  139. }
  140. }
  141. if (lodIndex == 0)
  142. {
  143. if (collisionLODMesh.hasMesh)
  144. {
  145. meshCollider.sharedMesh = collisionLODMesh.mesh;
  146. }
  147. else if (!collisionLODMesh.hasRequestedMesh)
  148. {
  149. collisionLODMesh.RequestMesh(mapData);
  150. }
  151. terrainChunksVisibleLastUpdate.Add(this);
  152. }
  153. }
  154. SetVisible(visible);
  155. }
  156. }
  157. public void SetVisible(bool visible)
  158. {
  159. if (meshObject != null)
  160. {
  161. meshObject.SetActive(visible);
  162. }
  163. }
  164. public bool IsVisible()
  165. {
  166. return meshObject.activeSelf;
  167. }
  168. }
  169. class LODMesh
  170. {
  171. public Mesh mesh;
  172. public bool hasRequestedMesh;
  173. public bool hasMesh;
  174. int lod;
  175. System.Action updateCallback;
  176. public LODMesh(int lod, System.Action updateCallback)
  177. {
  178. this.lod = lod;
  179. this.updateCallback = updateCallback;
  180. }
  181. void OnMeshDataReceived(MeshData meshData)
  182. {
  183. mesh = meshData.CreateMesh();
  184. hasMesh = true;
  185. updateCallback();
  186. }
  187. public void RequestMesh(MapData mapData)
  188. {
  189. hasRequestedMesh = true;
  190. mapGenerator.RequestMeshData(mapData, lod, OnMeshDataReceived);
  191. }
  192. }
  193. [System.Serializable]
  194. public struct LODInfo
  195. {
  196. public int lod;
  197. public float visibleDstThreshold;
  198. public bool useForCollider;
  199. }
  200. }