Skip to content

Instantly share code, notes, and snippets.

@tsaodown
Created May 18, 2016 05:08
Show Gist options
  • Save tsaodown/69aae514630255a637095fa83cc10422 to your computer and use it in GitHub Desktop.
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
using UnityEngine;
using Utilities;
public interface INoise2DGenerator {
float[,] GenerateNoise(Vector2 position, Vector2Int dimensions);
}
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;
}
}
}
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();
}
}
}
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;
}
}
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