Created
May 18, 2016 05:08
-
-
Save tsaodown/69aae514630255a637095fa83cc10422 to your computer and use it in GitHub Desktop.
DerpCraft - Building a MineCraft Clone in Unity - Noisemap Generation | Full code available at https://git.io/vrl5m
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using UnityEngine; | |
using Utilities; | |
public interface INoise2DGenerator { | |
float[,] GenerateNoise(Vector2 position, Vector2Int dimensions); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public static class Noise2DGenerator { | |
public static INoise2DGenerator Instance { get; private set; } | |
public static class Noise2DGeneratorFactory { | |
public static INoise2DGenerator CreatePerlinNoise2DGenerator( | |
int seed, float scale, int octaves, | |
float lacunarity, float persistence) { | |
Instance = new PerlinNoise2DGenerator(seed, scale, octaves, | |
lacunarity, persistence); | |
return Instance; | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using UnityEditor; | |
using UnityEngine; | |
using Utilities; | |
[RequireComponent(typeof(MeshFilter))] | |
[RequireComponent(typeof(MeshRenderer))] | |
public class NoiseDisplay : MonoBehaviour { | |
public int seed; | |
public float noiseScale = 100; | |
[Range(1, 10)] | |
public int octaves = 3; | |
public float lacunarity = 2.5f; | |
[Range(0, 1)] | |
public float persistence = 0.5f; | |
public Vector2 position; | |
public Vector2Int dimensions = Vector2Int.one * 10; | |
public bool autoUpdate = true; | |
bool isInitialSetupDone; | |
// Use this for initialization | |
void Start() { | |
GenerateNoiseMap(); | |
} | |
void OnValidate() { | |
if (noiseScale < 1) | |
noiseScale = 1; | |
if (octaves < 1) | |
octaves = 1; | |
if (lacunarity < 1) | |
lacunarity = 1; | |
if (dimensions.x < 1) | |
dimensions.x = 1; | |
if (dimensions.y < 1) | |
dimensions.y = 1; | |
if (!EditorApplication.isPlaying && autoUpdate) | |
if (dimensions.x > 0 && dimensions.y > 0) | |
GenerateNoiseMap(); | |
} | |
void GenerateNoiseMap() { | |
var meshData = new MeshData(false); | |
if (!isInitialSetupDone) { | |
GetComponent<MeshRenderer>().sharedMaterial = | |
new Material(Shader.Find("Mobile/Diffuse")); | |
meshData.AddVertex(new Vector3(-0.5f, 0, 0.5f)); | |
meshData.AddVertex(new Vector3(0.5f, 0, 0.5f)); | |
meshData.AddVertex(new Vector3(0.5f, 0, -0.5f)); | |
meshData.AddVertex(new Vector3(-0.5f, 0, -0.5f)); | |
meshData.AddQuadTriangles(); | |
meshData.uvs.Add(Vector2Int.up); | |
meshData.uvs.Add(Vector2Int.one); | |
meshData.uvs.Add(Vector2Int.right); | |
meshData.uvs.Add(Vector2Int.zero); | |
var filter = GetComponent<MeshFilter>(); | |
filter.sharedMesh = new Mesh(); | |
filter.sharedMesh.vertices = meshData.verts.ToArray(); | |
filter.sharedMesh.triangles = meshData.tris.ToArray(); | |
filter.sharedMesh.uv = meshData.uvs.ToArray(); | |
isInitialSetupDone = true; | |
} else { | |
meshData.AddVertex(new Vector3(-0.5f, 0, 0.5f)); | |
meshData.AddVertex(new Vector3(0.5f, 0, 0.5f)); | |
meshData.AddVertex(new Vector3(0.5f, 0, -0.5f)); | |
meshData.AddVertex(new Vector3(-0.5f, 0, -0.5f)); | |
GetComponent<MeshFilter>().sharedMesh.vertices = | |
meshData.verts.ToArray(); | |
} | |
Noise2DGenerator.Noise2DGeneratorFactory.CreatePerlinNoise2DGenerator( | |
seed, noiseScale, octaves, lacunarity, persistence); | |
float[,] noiseMap = | |
Noise2DGenerator.Instance.GenerateNoise(position, dimensions); | |
var pixels = new Color[dimensions.x * dimensions.y]; | |
for (int y = 0; y < dimensions.y; y++) { | |
for (int x = 0; x < dimensions.x; x++) { | |
pixels[y * dimensions.x + x] = | |
Color.Lerp(Color.black, Color.white, noiseMap[x, y]); | |
} | |
} | |
var tex = new Texture2D(dimensions.x, dimensions.y); | |
tex.wrapMode = TextureWrapMode.Clamp; | |
tex.filterMode = FilterMode.Point; | |
tex.SetPixels(pixels); | |
tex.Apply(); | |
GetComponent<MeshRenderer>().sharedMaterial.mainTexture = tex; | |
} | |
} | |
[CustomEditor(typeof(NoiseDisplay))] | |
public class NoiseDisplayEditor : Editor { | |
public override void OnInspectorGUI() { | |
if (!EditorApplication.isPlaying) { | |
DrawDefaultInspector(); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using UnityEngine; | |
using Utilities; | |
public class PerlinNoise2DGenerator : INoise2DGenerator { | |
// Possible range for random offset based on input seed. | |
static readonly int offsetRange = 100000; | |
// Seed for random offset in noise. | |
readonly int _seed; | |
// Scale for noise. Higher means we are more | |
// zoomed into the noise. | |
readonly float _scale; | |
// Number of layers of noise. | |
readonly int _octaves; | |
// Describes the amount each layered octave | |
// is zoomed out before layered. | |
readonly float _lacunarity; | |
// Describes how large of an effect zoomed | |
// out layers have on the final noise map. | |
readonly float _persistence; | |
public PerlinNoise2DGenerator(int seed, float scale, int octaves, | |
float lacunarity, float persistence) { | |
_seed = seed; | |
_scale = scale; | |
_octaves = octaves; | |
_lacunarity = lacunarity; | |
_persistence = persistence; | |
} | |
public float[,] GenerateNoise(Vector2 position, Vector2Int dimensions) { | |
var noiseMap = new float[dimensions.x, dimensions.y]; | |
// Generate random offset based on seed. | |
var prng = new System.Random(_seed); | |
float offsetX = prng.Next(-offsetRange, offsetRange); | |
float offsetY = prng.Next(-offsetRange, offsetRange); | |
// Calculate the maximum height given the octaves | |
// and persistence values. | |
float maxHeight = 0; | |
for (int o = 0; o < _octaves; o++) { | |
maxHeight += Mathf.Pow(_persistence, o); | |
} | |
// Get half dimensions to center noise. | |
Vector2Int halfDimensions = dimensions / 2; | |
for (int y = 0; y < dimensions.y; y++) { | |
for (int x = 0; x < dimensions.x; x++) { | |
float frequency = 1; | |
float amplitude = 1; | |
float height = 0; | |
// Generate a value for each layer of the noise map. | |
for (int o = 0; o < _octaves; o++) { | |
// Calculate position: | |
// (((Current point | |
// minus half dimensions for centered noise | |
// plus current position in noise) | |
// divided by noise scale for zoom into noise map) | |
// times the frequency to zoom in layers of noise) | |
// plus the random offset for different sections of noise | |
float sampleX = (((x - halfDimensions.x + position.x) / _scale) | |
* frequency) + offsetX; | |
float sampleY = (((y - halfDimensions.y + position.y) / _scale) | |
* frequency) + offsetY; | |
height += Mathf.PerlinNoise(sampleX, sampleY) * amplitude; | |
frequency *= _lacunarity; | |
amplitude *= _persistence; | |
} | |
// Inverse linear interpolation to generate a | |
// noise value between 0 and 1. | |
noiseMap[x, y] = Mathf.InverseLerp(0, maxHeight, height); | |
} | |
} | |
return noiseMap; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using UnityEditor; | |
using UnityEngine; | |
namespace Utilities { | |
// Convenience struct for integer based Vector2s. | |
[System.Serializable] | |
public struct Vector2Int { | |
public static Vector2Int zero { | |
get { | |
return new Vector2Int(0, 0); | |
} | |
} | |
public static Vector2Int one { | |
get { | |
return new Vector2Int(1, 1); | |
} | |
} | |
public static Vector2Int up { | |
get { | |
return new Vector2Int(0, 1); | |
} | |
} | |
public static Vector2Int down { | |
get { | |
return new Vector2Int(0, -1); | |
} | |
} | |
public static Vector2Int right { | |
get { | |
return new Vector2Int(1, 0); | |
} | |
} | |
public static Vector2Int left { | |
get { | |
return new Vector2Int(-1, 0); | |
} | |
} | |
public static Vector2Int operator +(Vector2Int a, Vector2Int b) { | |
Vector2 r = a.v2 + b.v2; | |
return new Vector2Int(Mathf.RoundToInt(r.x), Mathf.RoundToInt(r.y)); | |
} | |
public static Vector2 operator +(Vector2Int a, Vector2 b) { | |
return new Vector2(a.x + b.x, a.y + b.y); | |
} | |
public static Vector2Int operator -(Vector2Int a, Vector2Int b) { | |
Vector2 r = a.v2 - b.v2; | |
return new Vector2Int(Mathf.RoundToInt(r.x), Mathf.RoundToInt(r.y)); | |
} | |
public static Vector2Int operator *(Vector2Int v, int i) { | |
return new Vector2Int(v.x * i, v.y * i); | |
} | |
public static Vector2 operator *(Vector2Int v, float f) { | |
return new Vector2(v.x * f, v.y * f); | |
} | |
public static Vector2Int operator /(Vector2Int v, int i) { | |
return new Vector2Int(v.x / i, v.y / i); | |
} | |
public static Vector2Int operator /(Vector2Int v, float f) { | |
int i = Mathf.RoundToInt(f); | |
return new Vector2Int(v.x / i, v.y / i); | |
} | |
public static bool operator ==(Vector2Int a, Vector2Int b) { | |
return a.x == b.x && a.y == b.y; | |
} | |
public static bool operator !=(Vector2Int a, Vector2Int b) { | |
return !(a == b); | |
} | |
public static implicit operator Vector2Int(Vector2 v) { | |
return new Vector2Int(v); | |
} | |
public static implicit operator Vector2(Vector2Int v) { | |
return new Vector2(v.x, v.y); | |
} | |
[SerializeField] | |
Vector2 v2; | |
public float magnitude { | |
get { | |
return v2.magnitude; | |
} | |
} | |
public int x { | |
get { | |
return Mathf.RoundToInt(v2.x); | |
} | |
set { | |
v2.x = value; | |
} | |
} | |
public int y { | |
get { | |
return Mathf.RoundToInt(v2.y); | |
} | |
set { | |
v2.y = value; | |
} | |
} | |
public Vector3Int XYPlane { | |
get { | |
return new Vector3Int(x, y, 0); | |
} | |
} | |
public Vector3Int XZPlane { | |
get { | |
return new Vector3Int(x, 0, y); | |
} | |
} | |
public Vector3Int YZPlane { | |
get { | |
return new Vector3Int(0, x, y); | |
} | |
} | |
public Vector2Int(int x, int y) { | |
v2 = new Vector2(x, y); | |
} | |
public Vector2Int(Vector2 v) { | |
v2 = new Vector2(Mathf.RoundToInt(v.x), Mathf.RoundToInt(v.y)); | |
} | |
public override bool Equals(object obj) { | |
return base.Equals(obj); | |
} | |
public override int GetHashCode() { | |
return base.GetHashCode(); | |
} | |
public override string ToString() { | |
return string.Format("({0}, {1})", x, y); | |
} | |
} | |
public static class Vector2Utilities { | |
public static Vector3 XYPlane(Vector2 v) { | |
return new Vector3(v.x, v.y, 0); | |
} | |
public static Vector3 XZPlane(Vector2 v) { | |
return new Vector3(v.x, 0, v.y); | |
} | |
public static Vector3 YZPlane(Vector2 v) { | |
return new Vector3(0, v.x, v.y); | |
} | |
} | |
[CustomPropertyDrawer(typeof(Vector2Int))] | |
public class Vector2IntDrawer : PropertyDrawer { | |
public override void OnGUI(Rect position, SerializedProperty property, | |
GUIContent label) { | |
EditorGUI.BeginProperty(position, label, property); | |
Vector2Int result = | |
EditorGUI.Vector2Field(position, label, | |
property.FindPropertyRelative("v2").vector2Value); | |
property.FindPropertyRelative("v2").vector2Value = result; | |
EditorGUI.EndProperty(); | |
} | |
} | |
// Convenience struct for integer based Vector3s. | |
[System.Serializable] | |
public struct Vector3Int { | |
public static Vector3Int zero { | |
get { | |
return new Vector3Int(0, 0, 0); | |
} | |
} | |
public static Vector3Int one { | |
get { | |
return new Vector3Int(1, 1, 1); | |
} | |
} | |
public static Vector3Int up { | |
get { | |
return new Vector3Int(0, 1, 0); | |
} | |
} | |
public static Vector3Int down { | |
get { | |
return new Vector3Int(0, -1, 0); | |
} | |
} | |
public static Vector3Int north { | |
get { | |
return new Vector3Int(0, 0, 1); | |
} | |
} | |
public static Vector3Int south { | |
get { | |
return new Vector3Int(0, 0, -1); | |
} | |
} | |
public static Vector3Int east { | |
get { | |
return new Vector3Int(1, 0, 0); | |
} | |
} | |
public static Vector3Int west { | |
get { | |
return new Vector3Int(-1, 0, 0); | |
} | |
} | |
public static Vector3Int operator +(Vector3Int a, Vector3Int b) { | |
Vector3 r = a.v3 + b.v3; | |
return new Vector3Int( | |
Mathf.RoundToInt(r.x), Mathf.RoundToInt(r.y), Mathf.RoundToInt(r.z)); | |
} | |
public static Vector3 operator +(Vector3Int a, Vector3 b) { | |
return new Vector3(a.x + b.x, a.y + b.y, a.z + b.z); | |
} | |
public static Vector3 operator +(Vector3 a, Vector3Int b) { | |
return new Vector3(a.x + b.x, a.y + b.y, a.z + b.z); | |
} | |
public static Vector3Int operator -(Vector3Int a, Vector3Int b) { | |
Vector3 r = a.v3 - b.v3; | |
return new Vector3Int( | |
Mathf.RoundToInt(r.x), Mathf.RoundToInt(r.y), Mathf.RoundToInt(r.z)); | |
} | |
public static Vector3Int operator *(Vector3Int v, int i) { | |
return new Vector3Int(v.x * i, v.y * i, v.z * i); | |
} | |
public static Vector3 operator *(Vector3Int v, float f) { | |
return new Vector3(v.x * f, v.y * f, v.z * f); | |
} | |
public static Vector3Int operator /(Vector3Int v, int i) { | |
return new Vector3Int(v.x / i, v.y / i, v.z / i); | |
} | |
public static Vector3Int operator /(Vector3Int v, float f) { | |
int i = Mathf.RoundToInt(f); | |
return new Vector3Int(v.x / i, v.y / i, v.z / i); | |
} | |
public static implicit operator Vector3Int(Vector3 v) { | |
return new Vector3Int(v); | |
} | |
public static implicit operator Vector3(Vector3Int v) { | |
return new Vector3(v.x, v.y, v.z); | |
} | |
[SerializeField] | |
Vector3 v3; | |
public float magnitude { | |
get { | |
return v3.magnitude; | |
} | |
} | |
public int x { | |
get { | |
return Mathf.RoundToInt(v3.x); | |
} | |
set { | |
v3.x = value; | |
} | |
} | |
public int y { | |
get { | |
return Mathf.RoundToInt(v3.y); | |
} | |
set { | |
v3.y = value; | |
} | |
} | |
public int z { | |
get { | |
return Mathf.RoundToInt(v3.z); | |
} | |
set { | |
v3.z = value; | |
} | |
} | |
public Vector3Int(int x, int y, int z) { | |
v3 = new Vector3(x, y, z); | |
} | |
public Vector3Int(Vector3 v) { | |
v3 = new Vector3( | |
Mathf.RoundToInt(v.x), Mathf.RoundToInt(v.y), Mathf.RoundToInt(v.z)); | |
} | |
public override string ToString() { | |
return string.Format("({0}, {1}, {2})", x, y, z); | |
} | |
} | |
public static class Vector3Utilities { | |
public static Vector2 XYPlane(Vector3 v) { | |
return new Vector2(v.x, v.y); | |
} | |
public static Vector2 XZPlane(Vector3 v) { | |
return new Vector2(v.x, v.z); | |
} | |
public static Vector2 YZPlane(Vector3 v) { | |
return new Vector2(v.y, v.z); | |
} | |
} | |
[CustomPropertyDrawer(typeof(Vector3Int))] | |
public class Vector3IntDrawer : PropertyDrawer { | |
public override void OnGUI(Rect position, SerializedProperty property, | |
GUIContent label) { | |
EditorGUI.BeginProperty(position, label, property); | |
Vector3Int result = | |
EditorGUI.Vector3Field(position, label, | |
property.FindPropertyRelative("v3").vector3Value); | |
property.FindPropertyRelative("v3").vector3Value = result; | |
EditorGUI.EndProperty(); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment