├── LICENSE
├── README.md
├── results
├── DiamondSquare1.png
├── DiamondSquare2.png
├── DiamondSquare3.png
├── DiamondSquare4.png
└── DiamondSquare5.png
└── src
├── DiamondSquare.cs
├── MouseMovement.cs
├── RoughnessSlider.cs
└── WaterLevel.cs
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 Robert Stivanson
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Unity-Diamond-Square
2 | A lightweight Diamond-Square component for Unity. Last updated for Unity 5.4.2f2.
3 |
4 | # Diamond-Square
5 | The Diamond-square algorithm is a two-dimensional version of the Midpoint displacement algorithm. The algorithm is used to manipluate heightmaps to produce a natural-esk terrain.
6 |
7 | More details can be found at the following links:
8 | https://en.wikipedia.org/wiki/Diamond-square_algorithm
9 | http://www.paulboxley.com/blog/2011/03/terrain-generation-mark-one
10 | http://stevelosh.com/blog/2016/06/diamond-square/
11 |
12 | # How To Use
13 | 1. Import the script into an existing Unity project.
14 | 2. Attach the script to a GameObject either in the inspector or during runtime.
15 | 3. From a seperate script, during runtime, call the Reset function to flatten the terrain.
16 | 4. Call the ExecuteDiamondSquare function to execute the Diamond-Square algorithm.
17 |
18 | # Results
19 | The following images show the result of this algorithm. The water depicted in the photos are NOT a result of the algorithm; this was added seperatly to show height.
20 |
21 | 
22 | 
23 | 
24 | 
25 | 
26 |
--------------------------------------------------------------------------------
/results/DiamondSquare1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RStivanson/unity-diamond-square/ac0a5ebcf6da90f5573bcf07f9a60168d553e1eb/results/DiamondSquare1.png
--------------------------------------------------------------------------------
/results/DiamondSquare2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RStivanson/unity-diamond-square/ac0a5ebcf6da90f5573bcf07f9a60168d553e1eb/results/DiamondSquare2.png
--------------------------------------------------------------------------------
/results/DiamondSquare3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RStivanson/unity-diamond-square/ac0a5ebcf6da90f5573bcf07f9a60168d553e1eb/results/DiamondSquare3.png
--------------------------------------------------------------------------------
/results/DiamondSquare4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RStivanson/unity-diamond-square/ac0a5ebcf6da90f5573bcf07f9a60168d553e1eb/results/DiamondSquare4.png
--------------------------------------------------------------------------------
/results/DiamondSquare5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RStivanson/unity-diamond-square/ac0a5ebcf6da90f5573bcf07f9a60168d553e1eb/results/DiamondSquare5.png
--------------------------------------------------------------------------------
/src/DiamondSquare.cs:
--------------------------------------------------------------------------------
1 | using UnityEngine; // MonoBehaviour, TerrainCollider, TerrainData
2 |
3 | ///
4 | /// When attached to a Terrain GameObject, it can be used to randomized the heights
5 | /// using the Diamond-Square algorithm
6 | ///
7 | [RequireComponent(typeof(TerrainCollider))]
8 | public class DiamondSquare : MonoBehaviour {
9 | // Data container for heights of a terrain
10 | private TerrainData data;
11 | // Size of the sides of a terrain
12 | private int size;
13 | // Flag to set random corner heights when terrain is reset
14 | private bool randomizeCornerValues = false;
15 | // 2D array of heights
16 | private float[,] heights;
17 | // Control variable to determine smoothness of heights
18 | private float roughness = 0.8f;
19 |
20 | ///
21 | /// Getters / Setters for the roughness value.
22 | ///
23 | public float Roughness {
24 | get { return roughness; }
25 | set { roughness = Mathf.Clamp(value, 0.001f, 1.999f); }
26 | }
27 |
28 | ///
29 | /// Used for initialization
30 | ///
31 | private void Awake() {
32 | data = transform.GetComponent().terrainData;
33 | size = data.heightmapWidth;
34 |
35 | SetSeed((int)Random.value);
36 | Reset();
37 |
38 | return;
39 | }
40 |
41 | ///
42 | /// Sets the seed of the random number generator
43 | ///
44 | /// A value that influences the random number generator
45 | public void SetSeed(int seed) {
46 | Random.InitState(seed);
47 |
48 | return;
49 | }
50 |
51 | ///
52 | /// Flips the value of the randomizeCornerValues flag
53 | ///
54 | public void ToggleRandomizeCornerValues() {
55 | randomizeCornerValues = !randomizeCornerValues;
56 |
57 | return;
58 | }
59 |
60 | ///
61 | /// Resets the values of the terrain. If randomizeCornerValues is true then the
62 | /// corner heights will be randomized, else it will be flat.
63 | ///
64 | public void Reset() {
65 | heights = new float[size, size];
66 |
67 | // If the corners need to be randomized
68 | if (randomizeCornerValues) {
69 | heights[0, 0] = Random.value;
70 | heights[size - 1, 0] = Random.value;
71 | heights[0, size - 1] = Random.value;
72 | heights[size - 1, size - 1] = Random.value;
73 | }
74 |
75 | // Update the terrain data
76 | data.SetHeights(0, 0, heights);
77 |
78 | return;
79 | }
80 |
81 | ///
82 | /// Executes the DiamondSquare algorithm on the terrain.
83 | ///
84 | public void ExecuteDiamondSquare() {
85 | heights = new float[size, size];
86 | float average, range = 0.5f;
87 | int sideLength, halfSide, x, y;
88 |
89 | // While the side length is greater than 1
90 | for (sideLength = size - 1; sideLength > 1; sideLength /= 2) {
91 | halfSide = sideLength / 2;
92 |
93 | // Run Diamond Step
94 | for (x = 0; x < size - 1; x += sideLength) {
95 | for (y = 0; y < size - 1; y += sideLength) {
96 | // Get the average of the corners
97 | average = heights[x, y];
98 | average += heights[x + sideLength, y];
99 | average += heights[x, y + sideLength];
100 | average += heights[x + sideLength, y + sideLength];
101 | average /= 4.0f;
102 |
103 | // Offset by a random value
104 | average += (Random.value * (range * 2.0f)) - range;
105 | heights[x + halfSide, y + halfSide] = average;
106 | }
107 | }
108 |
109 | // Run Square Step
110 | for (x = 0; x < size - 1; x += halfSide) {
111 | for (y = (x + halfSide) % sideLength; y < size - 1; y += sideLength) {
112 | // Get the average of the corners
113 | average = heights[(x - halfSide + size - 1) % (size - 1), y];
114 | average += heights[(x + halfSide) % (size - 1), y];
115 | average += heights[x, (y + halfSide) % (size - 1)];
116 | average += heights[x, (y - halfSide + size - 1) % (size - 1)];
117 | average /= 4.0f;
118 |
119 | // Offset by a random value
120 | average += (Random.value * (range * 2.0f)) - range;
121 |
122 | // Set the height value to be the calculated average
123 | heights[x, y] = average;
124 |
125 | // Set the height on the opposite edge if this is
126 | // an edge piece
127 | if (x == 0) {
128 | heights[size - 1, y] = average;
129 | }
130 |
131 | if (y == 0) {
132 | heights[x, size - 1] = average;
133 | }
134 | }
135 | }
136 |
137 | // Lower the random value range
138 | range -= range * 0.5f * roughness;
139 | }
140 |
141 | // Update the terrain heights
142 | data.SetHeights(0, 0, heights);
143 |
144 | return;
145 | }
146 |
147 | ///
148 | /// Returns the amount of vertices to skip using the given depth.
149 | ///
150 | /// The vertice detail depth on the height array
151 | /// Amount of vertices to skip
152 | public int GetStepSize(int depth) {
153 | // Return an invalid step size if the depth is invalid
154 | if (!ValidateDepth(depth)) {
155 | return -1;
156 | }
157 |
158 | // Return the amount of vertices to skip
159 | return (int)((size - 1) / Mathf.Pow(2, (depth - 1)));
160 | }
161 |
162 | ///
163 | /// Returns the maximum depth for this terrain's size.
164 | ///
165 | /// Maximum depth for this terrain
166 | public int MaxDepth() {
167 | // 0.69314718056f = Natural Log of 2
168 | return (int)((Mathf.Log(size - 1) / 0.69314718056f) + 1);
169 | }
170 |
171 | ///
172 | /// Returns false if the depth is above zero and below maximum depth, true otheriwse
173 | ///
174 | /// The vertice detail depth on the height array
175 | ///
176 | private bool ValidateDepth(int depth) {
177 | if (depth > 0 && depth <= MaxDepth()) {
178 | return true;
179 | }
180 |
181 | return false;
182 | }
183 | }
184 |
--------------------------------------------------------------------------------
/src/MouseMovement.cs:
--------------------------------------------------------------------------------
1 | using UnityEngine; // MonoBehaviour
2 |
3 | ///
4 | /// Grabs the mouses movement and updates the camera as the mouse moves. This actually can be used on any object but
5 | /// for this project it is used on a camera object.
6 | ///
7 | [DisallowMultipleComponent]
8 | public class MouseMovement : MonoBehaviour {
9 | // Where we want our camera to focus
10 | Vector3 lookAt = Vector3.zero;
11 |
12 | // Current distance between lookAt and the camera
13 | float distance;
14 | // Camera movement speed in the x direction
15 | float xSpeed = 120.0f;
16 | // Camera movement speed in the y direction
17 | float ySpeed = 120.0f;
18 | // Camera zoom speed
19 | float scrollSpeed = 75.0f;
20 |
21 | float x = 0.0f, y = 0.0f;
22 |
23 | ///
24 | /// Used for initialization, this is called when the script is created before any Start functions.
25 | ///
26 | void Awake() {
27 | // Get the current camera angles in euler angles
28 | x = transform.eulerAngles.y;
29 | y = transform.eulerAngles.x;
30 |
31 | // Calculate the distance between lookAt and the camera
32 | distance = Vector3.Distance(transform.position, lookAt);
33 | return;
34 | }
35 |
36 | ///
37 | /// Called every frame, updates the camera position and rotation.
38 | ///
39 | void Update() {
40 | // Cameras current rotation
41 | Quaternion rotation = transform.rotation;
42 | // Cameras current positon
43 | Vector3 newPosition = transform.position;
44 |
45 | // Adjust the lookAt position
46 | if (Input.GetKeyDown(KeyCode.UpArrow)) {
47 | lookAt.y += 10;
48 | } else if (Input.GetKeyDown(KeyCode.DownArrow)) {
49 | lookAt.y -= 10;
50 | }
51 |
52 | // Adjust the distance to lookAt based on the mouse scroll wheel movement
53 | distance -= Input.GetAxis("Mouse ScrollWheel") * scrollSpeed;
54 |
55 | // If we are clicking the left mouse button
56 | if (Input.GetMouseButton(0)) {
57 | // Get the direction the mouse is moving and modify it by our speeds
58 | // Time.deltaTime is to keep speeds normal looking through frame lags
59 | x += Input.GetAxis("Mouse X") * xSpeed * Time.deltaTime;
60 | y -= Input.GetAxis("Mouse Y") * ySpeed * Time.deltaTime;
61 |
62 | // Convert the requested movements into a Quaternion rotation
63 | rotation = Quaternion.Euler(y, x, 0);
64 | }
65 |
66 | // Calculate the new position based on rotation and distance from lookAt
67 | newPosition = rotation * new Vector3(0.0f, 0.0f, -distance) + lookAt;
68 |
69 | // Update the cameras position and rotation
70 | transform.rotation = rotation;
71 | transform.position = newPosition;
72 |
73 | return;
74 | }
75 | }
--------------------------------------------------------------------------------
/src/RoughnessSlider.cs:
--------------------------------------------------------------------------------
1 | using UnityEngine; // MonoBehaviour
2 | using UnityEngine.UI; // Slider
3 |
4 | ///
5 | /// This class is to be attached to a slider object and given an object with the
6 | /// DiamondSqaure script attached.
7 | ///
8 | [RequireComponent(typeof(Slider))]
9 | public class RoughnessSlider : MonoBehaviour {
10 | // The DiamondSquare script to be adjusted
11 | public DiamondSquare diamondSquare;
12 | // The slider to get the values from
13 | private Slider slider;
14 |
15 | ///
16 | /// Used for initialization.
17 | ///
18 | public void Start() {
19 | slider = GetComponent();
20 | slider.value = diamondSquare.Roughness;
21 |
22 | return;
23 | }
24 |
25 | ///
26 | /// Sets the roughness in the DiamondSquare script to the value of the slider
27 | ///
28 | public void SetRoughness() {
29 | diamondSquare.Roughness = slider.value;
30 |
31 | return;
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/WaterLevel.cs:
--------------------------------------------------------------------------------
1 | using UnityEngine; // MonoBehaviour, Transform, and Vector3
2 | using UnityEngine.UI; // Slider
3 |
4 | ///
5 | /// This class is to be attached to a slider object and given an object that
6 | /// represents the water.
7 | ///
8 | [RequireComponent(typeof(Slider))]
9 | public class WaterLevel : MonoBehaviour {
10 | // The object to be adjusted
11 | public Transform water;
12 | // The slider to get the values from
13 | private Slider slider;
14 |
15 | ///
16 | /// Used for initialization.
17 | ///
18 | public void Start() {
19 | // Get the slider component and sync it with the water object
20 | slider = GetComponent();
21 | slider.value = water.position.y;
22 |
23 | return;
24 | }
25 |
26 | ///
27 | /// Adjusts the Y position of the water object.
28 | ///
29 | public void ChangeWaterLevel() {
30 | // Get the waters current position and change its y value
31 | Vector3 newPosition = water.position;
32 | newPosition.y = slider.value;
33 |
34 | // Update the waters position
35 | water.position = newPosition;
36 |
37 | return;
38 | }
39 | }
--------------------------------------------------------------------------------