Unity地形设计之midpoint displacement算法生成地形(十)

上一篇我们了解了该算法的大致计算过程,下面我们通过midpoint displacement算法(Diamond Step、Square step)创建地形

                                  (一)、Diamond Step

先计算第一个Diamond Step:

结果:

解释一下:

至此第一阶段:上面的第一个Diamond Step完成。如果我们通过不断重复这些Diamond Steps,它指最终建立起来并访问地形的每个顶点。

计算多个Diamond Step:

 此时地形表现形式如下:

上面目前看起来还不是随机地形,我们添加一些随机值,比如随机高度值:

结果每个点高度总是很接近最高高度,这时我们要做的是添加阻尼效果:

                                (二)、Square Step

Square Step:分别计算上图4个小黄点位置的高度

最后附上最终代码:

using UnityEngine;
public class CustomTerrain : MonoBehaviour {
    public bool resetTerrain = true;
    //改变地形随机高度数值
    public float MPDheightMin = -2f;
    public float MPDheightMax = 2f;
    //添加阻尼值
    public float MPDheightDampenerPower = 2.0f;
    //阻尼值受粗糙度的影响
    public float MPDroughness = 2.0f;
    float[,] GetHeightMap() //这个函数每次创建新的高度图时使用
    {
        if (!resetTerrain)
        {
         return terrainData.GetHeights(0, 0, terrainData.heightmapWidth, terrainData.heightmapHeight);
        }
        else
            return new float[terrainData.heightmapWidth, terrainData.heightmapHeight];
    }  
    public Terrain terrain; //获取地形(物体)对象   
    public TerrainData terrainData;//TerrainData里有地形所有的数据 
    private void OnEnable()
    {       
        terrain = gameObject.GetComponent<Terrain>(); //初始化地形数据 先获取地形再获取本身的地形数据
        terrainData = Terrain.activeTerrain.terrainData;
    }
    public void MidPointDisplacement()
    {
        float[,] heightMap = GetHeightMap();//获得高度图
        int width = terrainData.heightmapWidth - 1;//获得宽度
        int squareSize = width;//由获得的宽度设置正方形,之所以减1,是因为要围绕网格外部创建正方形    
        int cornerX, cornerY;
        int midX, midY;
        int pmidXL, pmidXR, pmidYU, pmidYD;//Square Step变量
        float heightMin = MPDheightMin;
        float heightMax = MPDheightMax;
        float heightDampener = (float)Mathf.Pow(MPDheightDampenerPower, -1 * MPDroughness);
        //设置网格的(大正方形的已知的四个顶点数值),即地形的四个角的随机高度
        //之所以注释掉是减少地形四角的随机性
        //heightMap[0, 0] = UnityEngine.Random.Range(0f, 0.2f);
        //heightMap[0, terrainData.heightmapHeight - 2] = Random.Range(0f, 0.2f);
        //heightMap[terrainData.heightmapWidth - 2, 0] = Random.Range(0f, 0.2f);
        //heightMap[terrainData.heightmapWidth - 2, terrainData.heightmapHeight - 2] = Random.Range(0f, 0.2f);
        while (squareSize > 0)
        {
            #region Diamnod Step
            for (int x = 0; x < width; x += squareSize)//实际上是跨出一个完整的正方形以获得下一个值
            {
                for (int y = 0; y < width; y += squareSize) //设置地形中间点的随机高度
                {
                    cornerX = (x + squareSize);
                    cornerY = (y + squareSize);
                    midX = (int)(x + squareSize / 2.0f);
                    midY = (int)(y + squareSize / 2.0f);
                    heightMap[midX, midY] = (float)((heightMap[x, y] + heightMap[cornerX, y] + heightMap[x, cornerY]
                    + heightMap[cornerX, cornerY]) / 4.0f + UnityEngine.Random.Range(heightMin, heightMax));

                }
            }
            #endregion
            #region Square Step
            for (int x = 0; x < width; x += squareSize)
            {
                for (int y = 0; y < width; y += squareSize)
                {
                    cornerX = (x + squareSize);
                    cornerY = (y + squareSize);
                    midX = (int)(x + squareSize / 2.0f);
                    midY = (int)(y + squareSize / 2.0f);
                    pmidXR = (int)(midX + squareSize);
                    pmidYU = (int)(midY + squareSize);
                    pmidXL = (int)(midX - squareSize);
                    pmidYD = (int)(midY - squareSize);
                //下面这句话是如果点正好处于地形的边缘,我们不往下进行,继续下一轮循环,阻止我们得到错误索引
                    if (pmidXL <= 0 || pmidYD <= 0
                        || pmidXR >= width - 1 || pmidYU >= width - 1) continue;
                    //Calculate the square value for the bottom side  
                    heightMap[midX, y] = (float)((heightMap[midX, midY] +
                                                  heightMap[x, y] +
                                                  heightMap[midX, pmidYD] +
                                                  heightMap[cornerX, y]) / 4.0f +
                                                 UnityEngine.Random.Range(heightMin, heightMax));
                    //Calculate the square value for the top side   
                    heightMap[midX, cornerY] = (float)((heightMap[x, cornerY] +
                                                            heightMap[midX, midY] +
                                                            heightMap[cornerX, cornerY] +
                                                        heightMap[midX, pmidYU]) / 4.0f +
                                                       UnityEngine.Random.Range(heightMin, heightMax));
                    //Calculate the square value for the left side   
                    heightMap[x, midY] = (float)((heightMap[x, y] +
                                                            heightMap[pmidXL, midY] +
                                                            heightMap[x, cornerY] +
                                                  heightMap[midX, midY]) / 4.0f +
                                                 UnityEngine.Random.Range(heightMin, heightMax));
                    //Calculate the square value for the right side   
                    heightMap[cornerX, midY] = (float)((heightMap[midX, y] +
                                                            heightMap[midX, midY] +
                                                            heightMap[cornerX, cornerY] +
                                                            heightMap[pmidXR, midY]) / 4.0f +
                                                       UnityEngine.Random.Range(heightMin, heightMax));

                }
            }
            #endregion
            squareSize = (int)(squareSize / 2.0f);
            //乘以阻尼值降低地形高度,同时减小最大值最小值
            heightMin *= heightDampener;
            heightMax *= heightDampener;
        }
            
        terrainData.SetHeights(0, 0, heightMap);
    }
}

using UnityEngine;
using UnityEditor;
[CustomEditor(typeof(CustomTerrain))]
public class CustomTerrainEditor : Editor {
    SerializedProperty resetTerrain;
   // 改变地形随机高度数值
    SerializedProperty MPDheightMin;
    SerializedProperty MPDheightMax;
    //阻尼值受粗糙度的影响
    SerializedProperty MPDheightDampenerPower;
    SerializedProperty MPDroughness;
    bool showMPD = false;
    private void OnEnable()
    {     
        resetTerrain = serializedObject.FindProperty("resetTerrain");
        MPDheightMin = serializedObject.FindProperty("MPDheightMin");
        MPDheightMax = serializedObject.FindProperty("MPDheightMax");
        MPDheightDampenerPower = serializedObject.FindProperty("MPDheightDampenerPower");
        MPDroughness = serializedObject.FindProperty("MPDroughness");

    }
    //绘制编辑面板
    public override void OnInspectorGUI()
    {
        //更新所有序列化的值
        serializedObject.Update();
        //获取自定义的地形属性脚本组件
        CustomTerrain terrain = (CustomTerrain)target;
        EditorGUILayout.PropertyField(resetTerrain);
        #region     
        showMPD = EditorGUILayout.Foldout(showMPD, "Midpoint Displacement");
        EditorGUILayout.PropertyField(MPDheightMin);
        EditorGUILayout.PropertyField(MPDheightMax);
        EditorGUILayout.PropertyField(MPDheightDampenerPower);
        EditorGUILayout.PropertyField(MPDroughness);
        if (showMPD)
        {
           
            if (GUILayout.Button("MPD"))
            {
                terrain.MidPointDisplacement();
            }
        }
        #endregion
        //应用发生的所有更改
        serializedObject.ApplyModifiedProperties();

    }
   
}
发布了122 篇原创文章 · 获赞 13 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/qq_40229737/article/details/103512393
今日推荐