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.01.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);
    }
}

色彩地图效果显示

请添加图片描述

基础网格生成规则

当三角网格数据绘制顺序为逆时针时:
123134  法线方向为朝上。
当三角网格数据绘制顺序为顺时针时:
143132  法线方向为朝下

请添加图片描述

法线方向:法线(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 查看本文章

请添加图片描述
请添加图片描述
请添加图片描述

暂时先这样吧,如果有时间的话就会更新,实在看不明白就留言,看到我会回复的。
路漫漫其修远兮,与君共勉。

猜你喜欢

转载自blog.csdn.net/weixin_43925843/article/details/126360515