Advertisement
Guest User

Untitled

a guest
Jul 26th, 2014
241
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 13.94 KB | None | 0 0
  1. using UnityEngine;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4.  
  5. public class DynamicWorld : MonoBehaviour {
  6.     DynamicChunk[,] worldChunks;
  7.    
  8.     public int worldSize = 15;
  9.     public int chunkSize = 5;
  10.     public int chunkHeight = 20;
  11.  
  12.     public int toolRadius = 5;
  13.     public float toolStrength = .01f;
  14.  
  15.     public Material defaultMaterial;
  16.    
  17.     //When an edge transitions between a positive and negative value, it'll be marked as "crossed"
  18.     public float surfaceCrossValue = 0;
  19.    
  20.     //The sacle of the noise for input into the system
  21.     public float noiseScaleFactor = 20;
  22.    
  23.     public Vector3 worldStartPosition;
  24.  
  25.     List<DynamicChunk> chunksToUpdate = new List<DynamicChunk>();
  26.  
  27.     void Start () {
  28.         worldChunks = new DynamicChunk[worldSize, worldSize];
  29.         worldStartPosition = GetPotentialStartPosition();
  30.         for(int i = 0; i < worldSize; i++) {
  31.             for(int j = 0; j < worldSize; j++) {
  32.  
  33.                 float chunkX = worldStartPosition.x + (i*chunkSize);
  34.                 float chunkY = 0;
  35.                 float chunkZ = worldStartPosition.z + (j*chunkSize);
  36.  
  37.                 worldChunks[i,j] = GenerateNewChunk(chunkX, chunkY, chunkZ);
  38.             }
  39.         }
  40.         for(int i = 0; i < worldSize; i++) {
  41.             for(int j = 0; j < worldSize; j++) {
  42.                 worldChunks[i,j].GenerateNewMesh(surfaceCrossValue);
  43.             }
  44.         }
  45.     }
  46.  
  47.     DynamicChunk GenerateNewChunk(float chunkX, float chunkY, float chunkZ){
  48.         float[,,] chunkData = new float[chunkSize, chunkHeight, chunkSize];
  49.  
  50.         FillData(ref chunkData, (int)chunkX, (int)chunkY, (int)chunkZ);
  51.  
  52.         GameObject chunk = new GameObject("Chunk" + chunkX + "," + chunkY + "," + chunkZ,
  53.                                           typeof(DynamicChunk), typeof(MeshFilter), typeof(MeshRenderer), typeof(MeshCollider));
  54.        
  55.         chunk.transform.position = new Vector3(chunkX, chunkY, chunkZ);
  56.         chunk.transform.parent = this.transform;
  57.         DynamicChunk dc = chunk.GetComponent<DynamicChunk>();
  58.         dc.Initialize(chunkData, chunkSize, chunkHeight, defaultMaterial, this);
  59.         return dc;
  60.     }
  61.  
  62.    
  63.     public float GetValue(int x, int y, int z) {
  64.         int relativeX = (int)(x - worldStartPosition.x);
  65.         int relativeZ = (int)(z - worldStartPosition.z);
  66.        
  67.         int chunkX = (int)(relativeX/chunkSize);
  68.         int chunkZ = (int)(relativeZ/chunkSize);
  69.        
  70.         if(chunkX >= 0 && chunkX < worldSize &&
  71.            y >= 0 && y < chunkHeight &&
  72.            chunkZ >= 0 && chunkZ < worldSize){
  73.             return worldChunks[chunkX, chunkZ].GetValue(
  74.                 (int)(x - worldStartPosition.x)%chunkSize,
  75.                 y,
  76.                 (int)(z - worldStartPosition.z)%chunkSize);
  77.         } else {
  78.             return GenerateDataValueForPoint(x,y,z);
  79.         }
  80.     }
  81.    
  82.     public void SetValue(int x, int y, int z, float value) {
  83.         int relativeX = (int)(x - worldStartPosition.x);
  84.         int relativeZ = (int)(z - worldStartPosition.z);
  85.        
  86.         int chunkX = (int)(relativeX/chunkSize);
  87.         int chunkZ = (int)(relativeZ/chunkSize);
  88.         if(chunkX >= 0 && chunkX < worldSize &&
  89.            chunkZ >= 0 && chunkZ < worldSize){
  90.             worldChunks[chunkX, chunkZ].SetValue(
  91.                 (int)(x - worldStartPosition.x)%chunkSize,
  92.                 y,
  93.                 (int)(z - worldStartPosition.z)%chunkSize,
  94.                 value);
  95.         }
  96.     }
  97.  
  98.  
  99.     void Update() {
  100.  
  101.         //Add density to a selected radius
  102.         if(Input.GetKey(KeyCode.Q)) {
  103.             Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
  104.             RaycastHit hitInfo;
  105.             if (Physics.Raycast (ray, out hitInfo))
  106.             {
  107.                 AddDensity(hitInfo.point, toolRadius);
  108.             }
  109.         }
  110.  
  111.         //Subtract density from a selected radius
  112.         if(Input.GetKey(KeyCode.E)) {
  113.             Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
  114.             RaycastHit hitInfo;
  115.             if (Physics.Raycast (ray, out hitInfo))
  116.             {
  117.                 SubDensity(hitInfo.point, toolRadius);
  118.             }
  119.         }
  120.  
  121.         //Apply a crater to the landscape
  122.         if(Input.GetKeyDown(KeyCode.C)) {
  123.             Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
  124.             RaycastHit hitInfo;
  125.             if (Physics.Raycast (ray, out hitInfo))
  126.             {
  127.                 Crater(hitInfo.point, toolRadius, 10);
  128.             }
  129.         }
  130.  
  131.         //Apply the flat tool to the landscape
  132.         if(Input.GetKey(KeyCode.F)) {
  133.             Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
  134.             RaycastHit hitInfo;
  135.             if (Physics.Raycast (ray, out hitInfo))
  136.             {
  137.                 MakeFlat(hitInfo.point, toolRadius, 10);
  138.             }
  139.         }
  140.  
  141.         //Update the world as we move around
  142.         Vector3 potentialStart = GetPotentialStartPosition();
  143.         if(worldStartPosition.x < potentialStart.x) {
  144.             MoveXPlus();
  145.         }
  146.         else if(worldStartPosition.x > potentialStart.x) {
  147.             MoveXMinus();
  148.         }
  149.        
  150.         if(worldStartPosition.z < potentialStart.z) {
  151.             MoveZPlus();
  152.         }
  153.         else if(worldStartPosition.z > potentialStart.z) {
  154.             MoveZMinus();
  155.         }
  156.  
  157.         //Rebuild the meshes for any chunks that need it
  158.         if(chunksToUpdate.Count > 0) {
  159.             foreach(DynamicChunk chunk in chunksToUpdate) {
  160.                 chunk.GenerateNewMesh(surfaceCrossValue);
  161.             }
  162.             chunksToUpdate.Clear();
  163.         }
  164.  
  165.         //Spawn a cube to check out the physics
  166.         if(Input.GetKeyDown(KeyCode.Space)) {
  167.             SpawnPrimitive(PrimitiveType.Cube);
  168.         }
  169.     }
  170.  
  171.     void FillData(ref float[,,] data, int xOffset, int yOffset, int zOffset) {
  172.         for (int x = 0; x < chunkSize; x++) {
  173.             for (int y = 0; y < chunkHeight; y++) {
  174.                 for (int z = 0; z < chunkSize; z++)
  175.                 {
  176.                     data[x,y,z] = GenerateDataValueForPoint(x + xOffset, y + yOffset, z + zOffset);
  177.                 }
  178.             }
  179.         }
  180.     }
  181.  
  182.     //Generates a density value for a given position
  183.     //Also useful for the edges of chunks where we don't have data saved yet, but want to retrieve what
  184.     // the data *will* be, for that edge.
  185.     private float GenerateDataValueForPoint(int x, int y, int z) {
  186.  
  187.         if(y >= chunkHeight-1) { //put a cap on things outside our bounds
  188.             return -1;
  189.         }
  190.        
  191.         if(y == 0) {//put a floor on the bottom
  192.             return 1;
  193.         }
  194.        
  195.         float dataX = x/noiseScaleFactor;
  196.         float dataY = y/noiseScaleFactor;
  197.         float dataZ = z/noiseScaleFactor;
  198.        
  199.         //Use the built in Perlin noise to generate some passable noise data.
  200.        
  201.         float retValue = Mathf.PerlinNoise(dataY,dataX+dataZ) - Mathf.PerlinNoise(dataX,dataZ);
  202.        
  203.         //Apply a gradient so our values are more likely to be:
  204.         // "air" (less than 0) at the top and "solid" (greater than 0) at the bottom
  205.         retValue -= (((float)y/chunkHeight)-.5f);
  206.         return retValue;
  207.     }
  208.  
  209.     void MoveXPlus() {
  210.         worldStartPosition.x += chunkSize;
  211.        
  212.         for(int i = 0; i < worldSize; i++) {
  213.             for(int j = 0; j < worldSize; j++) {
  214.                 if(i == worldSize - 1) {
  215.                     //Build a new column on the right
  216.                     worldChunks[i,j] = GenerateNewChunk(worldStartPosition.x + (i*chunkSize), 0, worldStartPosition.z + (j*chunkSize));
  217.                     chunksToUpdate.Add(worldChunks[i,j]);
  218.                     continue;
  219.                 } else if(i == 0) {
  220.                     //Free up the chunks at the x minus side
  221.                     worldChunks[i,j].Dispose();
  222.                 }
  223.                
  224.                 //Shift everything else in the arrays to the left
  225.                 worldChunks[i,j] = worldChunks[i+1,j];
  226.             }
  227.         }
  228.     }
  229.    
  230.     void MoveZPlus() {
  231.         worldStartPosition.z += chunkSize;
  232.        
  233.         for(int i = 0; i < worldSize; i++) {
  234.             for(int j = 0; j < worldSize; j++) {
  235.                 if(j == worldSize - 1) {
  236.                     //Build a new column on the top
  237.                     worldChunks[i,j] = GenerateNewChunk(worldStartPosition.x + (i*chunkSize), 0, worldStartPosition.z + (j*chunkSize));
  238.                     chunksToUpdate.Add(worldChunks[i,j]);
  239.                     continue;
  240.                 } else if(j == 0) {
  241.                     //Free up the chunks at the z minus side
  242.                     worldChunks[i,j].Dispose();
  243.                 }
  244.                
  245.                 //Shift everything else in the arrays down
  246.                 worldChunks[i,j] = worldChunks[i,j+1];
  247.             }
  248.         }
  249.     }
  250.    
  251.     void MoveXMinus() {
  252.         worldStartPosition.x -= chunkSize;
  253.        
  254.         for(int i = worldSize - 1; i >= 0; i--) {
  255.             for(int j = 0; j < worldSize; j++) {
  256.                 if(i == 0) {
  257.                     //Build a new column on the left
  258.                     worldChunks[i,j] = GenerateNewChunk(worldStartPosition.x + (i*chunkSize), 0, worldStartPosition.z + (j*chunkSize));
  259.                     chunksToUpdate.Add(worldChunks[i,j]);
  260.                     continue;
  261.                 } else if(i == worldSize - 1) {
  262.                     //Free up the chunks at the x plus side
  263.                     worldChunks[i,j].Dispose();
  264.                 }
  265.                
  266.                 //Shift everything else in the arrays to the right
  267.                 worldChunks[i,j] = worldChunks[i-1,j];
  268.             }
  269.         }
  270.     }
  271.    
  272.     void MoveZMinus() {
  273.         worldStartPosition.z -= chunkSize;
  274.        
  275.         for(int i = 0; i < worldSize; i++) {
  276.             for(int j = worldSize - 1; j >= 0; j--) {
  277.                 if(j == 0) {
  278.                     //Build a new column on the bottom
  279.                     worldChunks[i,j] = GenerateNewChunk(worldStartPosition.x + (i*chunkSize), 0, worldStartPosition.z + (j*chunkSize));
  280.                     chunksToUpdate.Add(worldChunks[i,j]);
  281.                     continue;
  282.                 } else if(j == worldSize - 1) {
  283.                     //Free up the chunks at the z plus side
  284.                     worldChunks[i,j].Dispose();
  285.                 }
  286.                
  287.                 //Shift everything else in the arrays up
  288.                 worldChunks[i,j] = worldChunks[i,j-1];
  289.             }
  290.         }
  291.     }
  292.  
  293.  
  294.     Vector3 ConvertCameraPosition() {
  295.         Vector3 camPos = Camera.main.transform.position;
  296.         return new Vector3(Mathf.Round(camPos.x/chunkSize)*chunkSize, 0, Mathf.Round(camPos.z/chunkSize)*chunkSize);
  297.     }
  298.    
  299.     Vector3 GetPotentialStartPosition() {
  300.         return new Vector3(ConvertCameraPosition().x-((worldSize*chunkSize)/2), 0, ConvertCameraPosition().z-((worldSize*chunkSize)/2));
  301.     }
  302.  
  303.     //Create a crater type effect in the landscape
  304.     //This works best on horizontal land. It will primarily reduce the density of the terrain
  305.     // except around the edges it will increase it slightly to provide the illusion of terrain being pushed out.
  306.     void Crater(Vector3 center, float radius, float modAmt) {
  307.         Vector3 currentPosition = new Vector3();
  308.         for(int i = Mathf.FloorToInt(center.x - radius); i < Mathf.CeilToInt(center.x + radius); i++) {
  309.             for(int j = Mathf.FloorToInt(center.y - radius); j < Mathf.CeilToInt(center.y + radius); j++) {
  310.                 for(int k = Mathf.FloorToInt(center.z - radius); k < Mathf.CeilToInt(center.z + radius); k++) {
  311.                     currentPosition.Set(i,j,k);
  312.                     float distance = Vector3.Distance(center, currentPosition);
  313.  
  314.                     if(j < center.y) {
  315.                         if(distance < radius) {
  316.                             ModDensityAt(i,j,k,modAmt*Mathf.Log((distance/radius)+.2f));
  317.                         }
  318.                     } else {
  319.                         if(distance < radius) {
  320.                             ModDensityAt(i,j,k,modAmt*Mathf.Log((distance/radius)));
  321.                         }
  322.                     }
  323.                 }
  324.             }
  325.         }
  326.     }
  327.  
  328.     //Gradually makes terrain flat. Uses the height of the center as the divide between ground and air.
  329.     void MakeFlat(Vector3 center, float radius, float modAmt) {
  330.         Vector3 currentPosition = new Vector3();
  331.         for(int i = Mathf.FloorToInt(center.x - radius); i < Mathf.CeilToInt(center.x + radius); i++) {
  332.             for(int j = Mathf.FloorToInt(center.y - radius); j < Mathf.CeilToInt(center.y + radius); j++) {
  333.                 for(int k = Mathf.FloorToInt(center.z - radius); k < Mathf.CeilToInt(center.z + radius); k++) {
  334.                     currentPosition.Set(i,j,k);
  335.                     //Everything above the center loses density
  336.                     //Everything below gains density
  337.                     if(j < center.y) {
  338.                         ModDensityAt(i,j,k,.01f);
  339.                     } else {
  340.                         ModDensityAt(i,j,k,-.01f);
  341.                     }
  342.                 }
  343.             }
  344.         }
  345.     }
  346.  
  347.     //A generic function for modifying the density of the terrain within a radius
  348.     void ModifyDensityFunction(Vector3 center, float radius, float modAmt) {
  349.         Vector3 currentPosition = new Vector3();
  350.         for(int i = Mathf.FloorToInt(center.x - radius); i < Mathf.CeilToInt(center.x + radius); i++) {
  351.             for(int j = Mathf.FloorToInt(center.y - radius); j < Mathf.CeilToInt(center.y + radius); j++) {
  352.                 for(int k = Mathf.FloorToInt(center.z - radius); k < Mathf.CeilToInt(center.z + radius); k++) {
  353.                     currentPosition.Set(i,j,k);
  354.                     if(Vector3.Distance(center, currentPosition) < radius) {
  355.                         ModDensityAt(i,j,k,modAmt);
  356.                     }
  357.                 }
  358.             }
  359.         }
  360.     }
  361.  
  362.     void AddDensity(Vector3 center, float radius) {
  363.         ModifyDensityFunction(center, radius, toolStrength);
  364.     }
  365.  
  366.     void SubDensity(Vector3 center, float radius) {
  367.         ModifyDensityFunction(center, radius, -toolStrength);
  368.     }
  369.  
  370.     //Used to change the density value at a specific data point
  371.     void ModDensityAt(int x, int y, int z, float value) {
  372.         int relativeX = (int)(x - worldStartPosition.x);
  373.         int relativeZ = (int)(z - worldStartPosition.z);
  374.  
  375.         DynamicChunk chunk = GetChunkContaining(x,z);
  376.  
  377.         if(chunk != null) {
  378.             int chunkOffsetX = (int)(x - worldStartPosition.x)%chunkSize;
  379.             int chunkOffsetZ = (int)(z - worldStartPosition.z)%chunkSize;
  380.  
  381.             if(chunkOffsetX >= 0 && chunkOffsetX < chunkSize &&
  382.                 y >= 0 && y < chunkHeight &&
  383.                 chunkOffsetZ >= 0 && chunkOffsetZ < chunkSize) {
  384.  
  385.                 float currentValue = chunk.GetValue(chunkOffsetX, y, chunkOffsetZ);
  386.                 chunk.SetValue(chunkOffsetX, y, chunkOffsetZ, Mathf.Max(-1, Mathf.Min (currentValue + value, 1)));
  387.                 AddChunkToUpdate(chunk);
  388.  
  389.                 //Check surrounding chunks for data changes.
  390.                 DynamicChunk neighborChunk;
  391.                 if(chunkOffsetX == 0) {
  392.                     neighborChunk = GetChunkContaining(x-chunkSize,z);
  393.                     AddChunkToUpdate(neighborChunk);
  394.                 }
  395.                 if(chunkOffsetZ == 0) {
  396.                     neighborChunk = GetChunkContaining(x,z-chunkSize);
  397.                     AddChunkToUpdate(neighborChunk);
  398.                 }
  399.  
  400.                 if(chunkOffsetX == chunkSize) {
  401.                     neighborChunk = GetChunkContaining(x+chunkSize,z);
  402.                     AddChunkToUpdate(neighborChunk);
  403.                 }
  404.                 if(chunkOffsetZ == chunkSize) {
  405.                     neighborChunk = GetChunkContaining(x,z+chunkSize);
  406.                     AddChunkToUpdate(neighborChunk);
  407.                 }
  408.             }
  409.         }
  410.     }
  411.  
  412.     //Keep track of chunks that need their meshes rebuilt
  413.     void AddChunkToUpdate(DynamicChunk chunk) {
  414.         if(chunk != null) {
  415.             if(!chunksToUpdate.Contains(chunk)) {
  416.                 chunksToUpdate.Add(chunk);
  417.             }
  418.         }
  419.     }
  420.  
  421.     //Return the chunk that contains the given coordinates
  422.     DynamicChunk GetChunkContaining(int x, int z) {
  423.         int relativeX = (int)(x - worldStartPosition.x);
  424.         int relativeZ = (int)(z - worldStartPosition.z);
  425.  
  426.         int chunkX = (int)(relativeX/chunkSize);
  427.         int chunkZ = (int)(relativeZ/chunkSize);
  428.  
  429.         if(chunkX < worldSize && chunkX >= 0 &&
  430.            chunkZ < worldSize && chunkZ >= 0) {
  431.         return worldChunks[chunkX, chunkZ];
  432.         } else {
  433.             return null;
  434.         }
  435.     }
  436.  
  437.     //Spawn a rigid body object, and allow it to live for 10 seconds
  438.     void SpawnPrimitive(PrimitiveType type) {
  439.         Vector3 spawnPosition = MouseUtils.GetMouseWorldPositionAtDepth(10);
  440.         GameObject go = GameObject.CreatePrimitive(type);
  441.         go.transform.position = spawnPosition;
  442.         go.AddComponent<Rigidbody>();
  443.        
  444.         GameObject.Destroy(go, 10);
  445.     }
  446.  
  447. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement