Unity 随机 生成地形 (PerlinNoise 柏林噪声)
程序化地图生成
地图显示模块
三种不同的地图格式数据生成:
DrawNoiseMap() 噪声地图
DrawTexture() 色彩地图
DrawMesh() 网格地图
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// 地图显示
/// 将噪声贴图转换为纹理
/// </summary>
public class MapDisplay_ZH : MonoBehaviour
{
[Header("纹理渲染")]
public Renderer _TextureRender;
[Header("网格数据")]
public MeshFilter _MeshFilter;
[Header("网格渲染")]
public MeshRenderer _MeshRender;
/// <summary>
/// 噪声地图绘制
/// </summary>
/// <param 柏林噪声="_NoiseMap"></param>
public void DrawNoiseMap(float[,] _NoiseMap)
{
//地图宽度
int _Width = _NoiseMap.GetLength(0);
//地图高度
int _Height = _NoiseMap.GetLength(1);
//根据传入值 确定渲染纹理长宽
Texture2D _Texture = new Texture2D(_Width, _Height);
//获取噪波数组中的所有值
Color[] _ColourMap = new Color[_Width * _Height];
//设置每个像素点的颜色
for (int y = 0; y < _Width; y++)
{
for (int x = 0; x < _Height; x++)
{
//色彩取样
// Color 是一维数组 噪声是二维数组
//获取当前像素点所在位置 : Y值 乘 地图宽度 再加上 X 就是当前像素在噪声地图中的位置
//当前取样点的颜色
//由于只想要 黑白色域 所以使用 Color.black 和 Color.white
_ColourMap[y * _Width + x] = Color.Lerp(Color.black, Color.white, _NoiseMap[x, y]);
}
}
//色彩传递
//纹理贴图赋值
_Texture.SetPixels(_ColourMap);
_Texture.Apply();
//主纹理贴图赋值
_TextureRender.sharedMaterial.mainTexture = _Texture;
//地图大小赋值
_TextureRender.transform.localScale = new Vector3(_Width, 1, _Height);
}
/// <summary>
/// Texture 地图绘制
/// </summary>
/// <param 图像="_Texture"></param>
public void DrawTexture(Texture2D _Texture)
{
//渲染贴图赋予
_TextureRender.sharedMaterial.mainTexture = _Texture;
//渲染物体大小设置
_TextureRender.transform.localScale = new Vector3(_Texture.width, 1, _Texture.height);
}
/// <summary>
/// 网格地形绘制
/// </summary>
/// <param 地形网格="_MeshData"></param>
/// <param 地形贴图="_Texture2D"></param>
internal void DrawMesh(MeshData _MeshData, Texture2D _Texture2D)
{
_MeshFilter.sharedMesh = _MeshData.CreteMesh();
_MeshRender.sharedMaterial.mainTexture = _Texture2D;
}
}
柏林噪声地图模块
这个模块为基础模块:
返回值在0.0到1.0之间。(返回值可能略低于0.0或超过1.0。)
关键性代码: Mathf.PerlinNoise()
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// 柏林噪声地图生成
/// </summary>
public static class Noise_ZH
{
/// <summary>
/// 柏林噪声地图生成方法
/// </summary>
/// <param 地图宽度="_MapWidth"></param>
/// <param 地图高度="_MapHeight"></param>
/// <param 地图大小="_Scale"></param>
/// <param 种子="_Seed"></param>
/// <param 抵消="_Octaves"></param>
/// <param 持续="_Persistance"></param>
/// <param 空隙="_Lacunarity"></param>
/// <param 偏移抵消="_Offset"></param>///
/// <returns></returns>
public static float[,] GenerateNoiseMap(int _MapWidth, int _MapHeight, float _Scale, int _Seed, int _Octaves, float _Persistance, float _Lacunarity, Vector2 _Offset)
{
//噪声地图
float[,] _NoiseMap = new float[_MapWidth, _MapHeight];
//地图种子随机
System.Random _Prng = new System.Random(_Seed);
//抵消
Vector2[] _OctaveOffsets = new Vector2[_Octaves];
for (int i = 0; i < _Octaves; i++)
{
float _OffsetX = _Prng.Next(-10000, 10000) + _Offset.x;
float _OffsetY = _Prng.Next(-10000, 10000) + _Offset.y;
_OctaveOffsets[i] = new Vector2(_OffsetX, _OffsetY);
}
//避免地图 不存在
if (_Scale <= 0)
{
_Scale = 0.0001f;
}
//地图放大响应
float _HalfWidth = _MapWidth / 2;
float _HalfHeight = _MapHeight / 2;
//最大噪声高度
float _MaxNoiseHeight = float.MinValue;
//最小噪声高度
float _MinNoiseHeight = float.MaxValue;
for (int y = 0; y < _MapHeight; y++)
{
for (int x = 0; x < _MapWidth; x++)
{
//振幅
float _Amplitude = 1;
//频率
float _Frequency = 1;
//噪波高度
float _NoiseHeight = 0;
for (int i = 0; i < _Octaves; i++)
{
//地图单元取整
float _SampleX = (x - _HalfWidth) / _Scale * _Frequency + _OctaveOffsets[i].x;
float _SampleY = (y - _HalfHeight) / _Scale * _Frequency + _OctaveOffsets[i].y;
//根据传入参数 生成2D柏林噪声
float _PerlinValue = Mathf.PerlinNoise(_SampleX, _SampleY) * 2 - 1;
//噪波高度等于 柏林噪声 乘于 振幅
_NoiseHeight += _PerlinValue * _Amplitude;
//振幅变更
_Amplitude *= _Persistance;
//频率变更
_Frequency *= _Lacunarity;
}
//限值操作
if (_NoiseHeight > _MaxNoiseHeight)
{
_MaxNoiseHeight = _NoiseHeight;
}
else if (_NoiseHeight < _MinNoiseHeight)
{
_MinNoiseHeight = _NoiseHeight;
}
//传入地图数据
_NoiseMap[x, y] = _NoiseHeight;
}
}
//噪声贴图 最大值最小值 限定输出
//图像叠加
for (int y = 0; y < _MapHeight; y++)
{
for (int x = 0; x < _MapWidth; x++)
{
_NoiseMap[x, y] = Mathf.InverseLerp(_MinNoiseHeight, _MaxNoiseHeight, _NoiseMap[x, y]);
}
}
//返回地图数据
return _NoiseMap;
}
}
柏林噪声效果显示
彩色地图模块
色彩地图模块:
TextureFromColourMap() 纹理生成模块
TextureFromHeightMap() 高度生成模块
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// 纹理生成器
/// </summary>
public static class TextureGenerator_ZH
{
/// <summary>
/// 颜色地图的纹理
/// </summary>
/// <param 色彩地图="_ColourMap"></param>
/// <param 宽度="_Width"></param>
/// <param 高度="_Height"></param>
/// <returns></returns>
public static Texture2D TextureFromColourMap(Color[] _ColourMap, int _Width, int _Height)
{
Texture2D _Texture = new Texture2D(_Width, _Height);
//绘制模式 变更
//点过滤-纹理像素变得块状近距离
//边缘块状显示
//_Texture.filterMode = FilterMode.Point;
_Texture.filterMode = FilterMode.Point;
//纹理坐标缠绕模式
//将纹理夹紧到边缘的最后一个像素
_Texture.wrapMode = TextureWrapMode.Clamp;
//色彩传递
//纹理贴图赋值
_Texture.SetPixels(_ColourMap);
//必须要应用一下不然不响应
_Texture.Apply();
return _Texture;
}
/// <summary>
/// 高度贴图纹理
/// </summary>
/// <param 高度贴图="_HeightMap"></param>
/// <returns></returns>
public static Texture2D TextureFromHeightMap(float[,] _HeightMap)
{
//地图宽度
int _Width = _HeightMap.GetLength(0);
//地图高度
int _Height = _HeightMap.GetLength(1);
根据传入值 确定渲染纹理长宽
//Texture2D _Texture = new Texture2D(_Width, _Height);
//获取噪波数组中的所有值
Color[] _ColourMap = new Color[_Width * _Height];
//设置每个像素点的颜色
for (int y = 0; y < _Width; y++)
{
for (int x = 0; x < _Height; x++)
{
//色彩取样
// Color 是一维数组 噪声是二维数组
//获取当前像素点所在位置 : Y值 乘 地图宽度 再加上 X 就是当前像素在噪声地图中的位置
//当前取样点的颜色
//由于只想要 黑白色域 所以使用 Color.black 和 Color.white
_ColourMap[y * _Width + x] = Color.Lerp(Color.black, Color.white, _HeightMap[x, y]);
}
}
色彩传递
纹理贴图赋值
//_Texture.SetPixels(_ColourMap);
//_Texture.Apply();
return TextureFromColourMap(_ColourMap, _Width, _Height);
}
}
色彩地图效果显示
基础网格生成规则
当三角网格数据绘制顺序为逆时针时:
123、134 法线方向为朝上。
当三角网格数据绘制顺序为顺时针时:
143、132 法线方向为朝下
法线方向:法线(normal line),是指始终垂直于某平面的直线。在几何学中,法线指平面上垂直于曲线在某点的切线的一条线。法线也应用于光学的平面镜反射上。
如果法线方向背对视角方向,那么该网格三角面将不可视。
网格地图模块
网格地图模块:
MeshData 网格类
AddTrianle()为关键性方法
进行三角形网格绘制逻辑
CreteMesh()
网格数据填充方法
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// 网格生成器
/// </summary>
public static class MeshGenerator_ZH
{
/// <summary>
/// 地形网格生成
/// </summary>
/// <param 高度地图="_HeightMap"></param>
public static MeshData GenerateTerrainMesh(float[,] _HeightMap)
{
//宽度
int _Width = _HeightMap.GetLength(0);
//高度
int _Height = _HeightMap.GetLength(1);
//网格划分
float _TopLeftX = (_Width - 1) / -2f;
float _TopLeftZ = (_Height - 1) / -2f;
//网格数据
MeshData _MeshData = new MeshData(_Width, _Height);
//三角形 绘制序号
int _VertexIndex = 0;
//网格数据填充
for (int y = 0; y < _Height; y++)
{
for (int x = 0; x < _Width; x++)
{
//顶点数据填充
_MeshData._Vertices[_VertexIndex] = new Vector3(_TopLeftX + x, _HeightMap[x, y], _TopLeftZ + y);
//UV 数据填充
_MeshData._UVs[_VertexIndex] = new Vector2(x / (float)_Width, y / (float)_Height);
//剔除 行 列 最边缘 的顶点 不计入网格渲染计算
if (x < _Width - 1 && y < _Height - 1)
{
_MeshData.AddTrianle(_VertexIndex, _VertexIndex + _Width + 1, _VertexIndex + _Width);
_MeshData.AddTrianle(_VertexIndex + _Width + 1, _VertexIndex, _VertexIndex + 1);
}
//序号增加
_VertexIndex++;
}
}
return _MeshData;
}
}
/// <summary>
/// 网格数据
/// </summary>
public class MeshData
{
//顶点数据
public Vector3[] _Vertices;
//绘制序列
public int[] _Triangles;
//UV 数据
public Vector2[] _UVs;
//三角 序号
int _TriangleIndex;
public MeshData(int _MeshWidth,int _MeshHeight)
{
//网格顶点数据
_Vertices = new Vector3[_MeshWidth * _MeshHeight];
//UV 数据
_UVs = new Vector2[_MeshWidth * _MeshHeight];
//三角形绘制序列
_Triangles = new int[(_MeshWidth - 1) * (_MeshHeight - 1) * 6];
}
/// <summary>
/// 网格三角形绘制
/// </summary>
/// <param 顶点A="_A"></param>
/// <param 顶点B="_B"></param>
/// <param 顶点C="_C"></param>
public void AddTrianle(int _A,int _B,int _C)
{
//例如:
// 1 2 3
// 4 5 6
// 7 8 9
// 绘制 逻辑为 124 245 235 356 457 578 568 689 右手定则 逆时针 法线方向朝上
//注意 绘制的的方向要统一 统一顺时针绘制 或者逆时针绘制
_Triangles[_TriangleIndex] = _C;
_Triangles[_TriangleIndex+1] = _B;
_Triangles[_TriangleIndex+2] = _A;
_TriangleIndex += 3;
}
/// <summary>
/// 新建网格
/// </summary>
/// <returns></returns>
public Mesh CreteMesh()
{
//网格数据
Mesh _Mesh = new Mesh();
//顶点数据赋予
_Mesh.vertices = _Vertices;
//三角形序列赋予
_Mesh.triangles = _Triangles;
//UV 数据 赋予
_Mesh.uv = _UVs;
//使用默认法线
_Mesh.RecalculateNormals();
//数据返回
return _Mesh;
}
}
网格地图效果显示
程序化地图管理模块
用作管理地图模块化生成
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// 地图管理
/// </summary>
public class MapGenertor_ZH : MonoBehaviour
{
/// <summary>
/// 地图显示类型枚举
/// </summary>
public enum DrawMap {
NoiseMap, ColourMap, MeshMap }
[Header("地图绘制类型")]
public DrawMap _DrawMap = DrawMap.NoiseMap;
[Header("地图宽度")]
public int _MapWidth;
[Header("地图高度")]
public int _MapHeight;
[Header("地图大小")]
public float _NoiseScale;
[Header("八度")]
public int _Octaves;
[Header("持续")]
[Range(0, 1)]
public float _Persistance;
[Header("空隙")]
public float _Lacunarity;
[Header("地图种子")]
public int _Seed;
[Header("偏移")]
public Vector2 _Offset;
[Header("地形区域")]
public TerrainType[] _Regions;
[Header("自动更新布尔")]
public bool _AutoUpdate;
/// <summary>
/// 地图生成 调用
/// </summary>
public void GenerateMap()
{
//参数传递
float[,] _NoiseMap = Noise_ZH.GenerateNoiseMap(_MapWidth, _MapHeight, _NoiseScale, _Seed, _Octaves, _Persistance, _Lacunarity, _Offset);
//色彩地图
Color[] _ColourMap = new Color[_MapWidth * _MapHeight];
//根据地形高度进行色彩赋予
for (int y = 0; y < _MapHeight; y++)
{
for (int x = 0; x < _MapWidth; x++)
{
//获取当前地图高度值
float _CurrentHeight = _NoiseMap[x, y];
//进行地形区域判定
for (int i = 0; i < _Regions.Length; i++)
{
if (_CurrentHeight <= _Regions[i]._Height)
{
//色彩赋值
_ColourMap[y * _MapWidth + x] = _Regions[i]._Colour;
break;
}
}
}
}
//查找 MapDisplay_ZH 搭载物体
MapDisplay_ZH _Display = FindObjectOfType<MapDisplay_ZH>();
//根据不同类型进行不同地图绘制
if (_DrawMap == DrawMap.NoiseMap)
{
//进行柏林噪声地形绘制
_Display.DrawTexture(TextureGenerator_ZH.TextureFromHeightMap(_NoiseMap));
}
else if (_DrawMap == DrawMap.ColourMap)
{
//进行色彩贴图地形绘制
_Display.DrawTexture(TextureGenerator_ZH.TextureFromColourMap(_ColourMap, _MapWidth, _MapHeight));
}
else if (_DrawMap == DrawMap.MeshMap)
{
//进行网格地形绘制
_Display.DrawMesh(MeshGenerator_ZH.GenerateTerrainMesh(_NoiseMap), TextureGenerator_ZH.TextureFromColourMap(_ColourMap, _MapWidth, _MapHeight));
}
}
/// <summary>
/// 当脚本被加载或检查器在值被修改时调用此函数时(仅在编辑器调用中)
/// </summary>
private void OnValidate()
{
//保证初始化显示
if (_MapWidth < 1)
{
_MapWidth = 1;
}
if (_MapHeight < 1)
{
_MapHeight = 1;
}
if (_Lacunarity < 1)
{
_Lacunarity = 1;
}
if (_Octaves < 0)
{
_Octaves = 0;
}
}
}
//序列化
[System.Serializable]
/// <summary>
/// 地形类型
/// </summary>
public struct TerrainType
{
//名称
public string _Name;
//高度
public float _Height;
//颜色
public Color _Colour;
}
地图管理 菜单编辑
需要在 根目录创建一个 Editor 文件夹并把代码文件放进去才能执行
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
/// <summary>
/// 菜单编辑
/// </summary>
[CustomEditor(typeof(MapGenertor_ZH))]
public class MapGeneratorEditor_ZH : Editor
{
public override void OnInspectorGUI()
{
MapGenertor_ZH _MapGen = (MapGenertor_ZH)target;
//绘制内置检查器
//如果发现变更
if (DrawDefaultInspector())
{
//如果更新布尔为 Ture 就持续更新地图
if (_MapGen._AutoUpdate)
{
_MapGen.GenerateMap();
}
}
//在 MapGenertor_ZH 上创建更新按钮
if (GUILayout.Button("Generate"))
{
_MapGen.GenerateMap();
}
}
}
代码搭载
效果展示
根据地图种子的值来进行地图随机变换
扫描二维码关注公众号,回复:
14674736 查看本文章
![](/qrcode.jpg)
暂时先这样吧,如果有时间的话就会更新,实在看不明白就留言,看到我会回复的。
路漫漫其修远兮,与君共勉。