lcfr822

Middle Displacement Terrain 2D

Nov 25th, 2019
87
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 9.52 KB | None | 0 0
  1. using System.Collections;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4.  
  5. public class MDTerrainController2D : MonoBehaviour
  6. {
  7.     private GameObject detailObject;
  8.  
  9.     public float startHeight, endHeight, roughness, resolution;
  10.     public SpriteRenderer backgroundRenderer;
  11.     public Texture terrainTexture, detailTexture;
  12.  
  13.     private void Awake()
  14.     {
  15.  
  16.         float[] heightMap = GenerateHeightmap(startHeight, endHeight, Mathf.CeilToInt(backgroundRenderer.bounds.size.x) * Mathf.CeilToInt(resolution));
  17.         Vector2[] terrainVertices = CreateVertices(heightMap, resolution);
  18.         Vector2[] terrainUVs = GenerateTerrainUVs(heightMap);
  19.         int[] terrainTriangles = Triangulate(terrainVertices.Length);
  20.         GenerateTerrainMesh(terrainVertices, terrainTriangles, terrainUVs, "Generated Terrain", terrainTexture, 0);
  21.  
  22.         Vector2[] grassVertices = GenerateGrassVertices(heightMap, resolution);
  23.         Vector2[] grassUVs = GenerateGrassUVs(heightMap);
  24.         int[] grassTriangles = Triangulate(terrainVertices.Length);
  25.         CalculateCollider(GenerateTerrainMesh(grassVertices, grassTriangles, grassUVs, "Generated Terrain Detail", detailTexture, -2, out detailObject));
  26.     }
  27.  
  28.     private Mesh GenerateTerrainMesh(Vector2[] vertices, int[] triangles, Vector2[] uvs, string name, Texture texture, int z)
  29.     {
  30.         List<Vector3> meshVertices = new List<Vector3>();
  31.  
  32.         foreach(Vector2 vertex in vertices)
  33.         {
  34.             meshVertices.Add(new Vector3(vertex.x, vertex.y, z));
  35.         }
  36.  
  37.         Mesh mesh = new Mesh();
  38.         mesh.vertices = meshVertices.ToArray();
  39.         mesh.uv = uvs;
  40.         mesh.triangles = triangles;
  41.         mesh.RecalculateNormals();
  42.         mesh.RecalculateBounds();
  43.  
  44.         GameObject terrainObject = new GameObject(name);
  45.         terrainObject.AddComponent<MeshRenderer>();
  46.         MeshFilter meshFilter = terrainObject.AddComponent<MeshFilter>();
  47.         meshFilter.mesh = mesh;
  48.  
  49.         terrainObject.GetComponent<MeshRenderer>().material.SetTexture("_MainTex", texture);
  50.         terrainObject.transform.parent = transform;
  51.         terrainObject.transform.localPosition = transform.InverseTransformPoint(transform.position);
  52.  
  53.         return mesh;
  54.     }
  55.    
  56.     private Mesh GenerateTerrainMesh(Vector2[] vertices, int[] triangles, Vector2[] uvs, string name, Texture texture, int z, out GameObject meshObject)
  57.     {
  58.         List<Vector3> meshVertices = new List<Vector3>();
  59.  
  60.         foreach(Vector2 vertex in vertices)
  61.         {
  62.             meshVertices.Add(new Vector3(vertex.x, vertex.y, z));
  63.         }
  64.  
  65.         Mesh mesh = new Mesh();
  66.         mesh.vertices = meshVertices.ToArray();
  67.         mesh.uv = uvs;
  68.         mesh.triangles = triangles;
  69.         mesh.RecalculateNormals();
  70.         mesh.RecalculateBounds();
  71.  
  72.         GameObject terrainObject = new GameObject(name);
  73.         terrainObject.AddComponent<MeshRenderer>();
  74.         MeshFilter meshFilter = terrainObject.AddComponent<MeshFilter>();
  75.         meshFilter.mesh = mesh;
  76.  
  77.         terrainObject.GetComponent<MeshRenderer>().material.SetTexture("_MainTex", texture);
  78.         terrainObject.transform.parent = transform;
  79.         terrainObject.transform.localPosition = transform.InverseTransformPoint(transform.position);
  80.  
  81.         meshObject = terrainObject;
  82.         meshObject.AddComponent(typeof(PolygonCollider2D));
  83.         mesh.name = meshObject.name;
  84.  
  85.         return mesh;
  86.     }
  87.  
  88.     private float[] GenerateHeightmap(float startHeight, float endHeight, int length)
  89.     {
  90.         float[] heightmap = new float[length + 1];
  91.         heightmap[0] = startHeight;
  92.         heightmap[heightmap.Length - 1] = endHeight;
  93.  
  94.         GenerateMidpoint(0, heightmap.Length - 1, roughness, heightmap);
  95.         return heightmap;
  96.     }
  97.  
  98.     private void GenerateMidpoint(int start, int end, float passedRoughness, float[] heightmap)
  99.     {
  100.         int midpoint = Mathf.FloorToInt((start + end) / 2);
  101.  
  102.         if(midpoint != start)
  103.         {
  104.             float midHeight = (heightmap[start] + heightmap[end]) / 2;
  105.             heightmap[midpoint] = midHeight + Random.Range(-passedRoughness, passedRoughness);
  106.  
  107.             GenerateMidpoint(start, midpoint, passedRoughness / 2, heightmap);
  108.             GenerateMidpoint(midpoint, end, passedRoughness / 2, heightmap);
  109.         }
  110.     }
  111.  
  112.     private Vector2[] CreateVertices(float[] heightmap, float resolution)
  113.     {
  114.         resolution = Mathf.Max(1, resolution);
  115.  
  116.         List<Vector2> vertices = new List<Vector2>();
  117.         for(int i = 0; i < heightmap.Length; i++)
  118.         {
  119.             vertices.Add(new Vector2(i / resolution, heightmap[i]));
  120.             vertices.Add(new Vector2(i / resolution, 0));
  121.         }
  122.  
  123.         return vertices.ToArray();
  124.     }
  125.  
  126.     private Vector2[] GenerateTerrainUVs(float[] heightmap)
  127.     {
  128.         List<Vector2> uvs = new List<Vector2>();
  129.         float factor = 1.0f / heightmap.Length;
  130.  
  131.         for(int i = 0; i < heightmap.Length; i++)
  132.         {
  133.             uvs.Add(new Vector2(factor * i * 20, heightmap[i] / 20));
  134.             uvs.Add(new Vector2(factor * i * 20, 0));
  135.         }
  136.  
  137.         return uvs.ToArray();
  138.     }
  139.  
  140.     private Vector2[] GenerateGrassVertices(float[] heightmap, float resolution)
  141.     {
  142.         resolution = Mathf.Max(1, resolution);
  143.  
  144.         List<Vector2> vertices = new List<Vector2>();
  145.  
  146.         for(int i = 0; i < heightmap.Length; i++)
  147.         {
  148.             vertices.Add(new Vector2(i / resolution, heightmap[i]));
  149.             vertices.Add(new Vector2(i / resolution, heightmap[i] - 1));
  150.         }
  151.  
  152.         return vertices.ToArray();
  153.     }
  154.  
  155.     private Vector2[] GenerateGrassUVs(float[] heightmap)
  156.     {
  157.         List<Vector2> uvs = new List<Vector2>();
  158.         float factor = 1.0f / heightmap.Length;
  159.  
  160.         for(int i = 0; i < heightmap.Length; i++)
  161.         {
  162.             uvs.Add(new Vector2(i % 2, 1));
  163.             uvs.Add(new Vector2(i % 2, 0));
  164.         }
  165.  
  166.         return uvs.ToArray();
  167.     }
  168.  
  169.     private void CalculateCollider(Mesh mesh)
  170.     {
  171.         int[] triangles = mesh.triangles;
  172.         Vector3[] vertices = mesh.vertices;
  173.  
  174.         // Get just the outer edges from the mesh's triangles (ignore or remove any shared edges)
  175.         Dictionary<string, KeyValuePair<int, int>> edges = new Dictionary<string, KeyValuePair<int, int>>();
  176.         for (int i = 0; i < triangles.Length; i += 3)
  177.         {
  178.             for (int e = 0; e < 3; e++)
  179.             {
  180.                 int vert1 = triangles[i + e];
  181.                 int vert2 = triangles[i + e + 1 > i + 2 ? i : i + e + 1];
  182.                 string edge = Mathf.Min(vert1, vert2) + ":" + Mathf.Max(vert1, vert2);
  183.                 if (edges.ContainsKey(edge))
  184.                 {
  185.                     edges.Remove(edge);
  186.                 }
  187.                 else
  188.                 {
  189.                     edges.Add(edge, new KeyValuePair<int, int>(vert1, vert2));
  190.                 }
  191.             }
  192.         }
  193.  
  194.         // Create edge lookup (Key is first vertex, Value is second vertex, of each edge)
  195.         Dictionary<int, int> lookup = new Dictionary<int, int>();
  196.         foreach (KeyValuePair<int, int> edge in edges.Values)
  197.         {
  198.             if (lookup.ContainsKey(edge.Key) == false)
  199.             {
  200.                 lookup.Add(edge.Key, edge.Value);
  201.             }
  202.         }
  203.  
  204.         // Create empty polygon collider
  205.         PolygonCollider2D polygonCollider = detailObject.AddComponent(typeof(PolygonCollider2D)) as PolygonCollider2D;
  206.         polygonCollider.pathCount = 0;
  207.  
  208.         // Loop through edge vertices in order
  209.         int startVert = 0;
  210.         int nextVert = startVert;
  211.         int highestVert = startVert;
  212.         List<Vector2> colliderPath = new List<Vector2>();
  213.         while (true)
  214.         {
  215.  
  216.             // Add vertex to collider path
  217.             colliderPath.Add(vertices[nextVert]);
  218.  
  219.             // Get next vertex
  220.             nextVert = lookup[nextVert];
  221.  
  222.             // Store highest vertex (to know what shape to move to next)
  223.             if (nextVert > highestVert)
  224.             {
  225.                 highestVert = nextVert;
  226.             }
  227.  
  228.             // Shape complete
  229.             if (nextVert == startVert)
  230.             {
  231.  
  232.                 // Add path to polygon collider
  233.                 polygonCollider.pathCount++;
  234.                 polygonCollider.SetPath(polygonCollider.pathCount - 1, colliderPath.ToArray());
  235.                 colliderPath.Clear();
  236.  
  237.                 // Go to next shape if one exists
  238.                 if (lookup.ContainsKey(highestVert + 1))
  239.                 {
  240.  
  241.                     // Set starting and next vertices
  242.                     startVert = highestVert + 1;
  243.                     nextVert = startVert;
  244.  
  245.                     // Continue to next loop
  246.                     continue;
  247.                 }
  248.  
  249.                 for(int i = 0; i < polygonCollider.pathCount; i++)
  250.                 {
  251.                     detailObject.GetComponent<PolygonCollider2D>().SetPath(i, polygonCollider.GetPath(i));
  252.                 }
  253.  
  254.                 detailObject.AddComponent<Rigidbody2D>().isKinematic = true;
  255.                 // No more verts
  256.                 break;
  257.             }
  258.         }
  259.     }
  260.  
  261.     private int[] Triangulate(int count)
  262.     {
  263.         List<int> indices = new List<int>();
  264.  
  265.         for(int i = 0; i <= count - 4; i += 2)
  266.         {
  267.             indices.Add(i);
  268.             indices.Add(i + 3);
  269.             indices.Add(i + 1);
  270.  
  271.             indices.Add(i + 3);
  272.             indices.Add(i);
  273.             indices.Add(i + 2);
  274.         }
  275.  
  276.         return indices.ToArray();
  277.     }
  278. }
Add Comment
Please, Sign In to add comment