这篇我们设置多个Perlin地形,首先把需要用到的插件Editor GUI table导到项目中。
编辑CustomTerrain脚本:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
using System.Linq;
using UnityEditor;
public class CustomTerrain : MonoBehaviour {
public bool resetTerrain = true;
//MULTIPLE PERLIN --------------------
//这个类提供Perlin参数,由它我们可以创建perlin参数列表
[System.Serializable]
public class PerlinParameters
{
//存储缩放值
public float mPerlinXScale = 0.01f;
public float mPerlinYScale = 0.01f;
//分型布朗运动新增属性
public int mPerlinOctaves = 3;
public float mPerlinPersistance = 8;
public float mPerlinHeightScale = 0.09f;
public int mPerlinOffsetX = 0;
public int mPerlinOffsetY = 0;
public bool remove = false;
}
//创建我们的第一个列表
public List<PerlinParameters> perlinParameters = new List<PerlinParameters>()
{
new PerlinParameters()//列表有一个参数
};
//这个函数每次创建新的高度图时使用
float[,] GetHeightMap()
{
if (!resetTerrain)
{
return terrainData.GetHeights(0, 0, terrainData.heightmapWidth,
terrainData.heightmapHeight);
}
else
return new float[terrainData.heightmapWidth,
terrainData.heightmapHeight];
}
//获取地形(物体)对象
public Terrain terrain;
//TerrainData里有地形所有的数据
public TerrainData terrainData;
//每次编辑完脚本内容返回编辑器时运行后会执行此方法
private void OnEnable()
{
//初始化地形数据 先获取地形再获取本身的地形数据
terrain = gameObject.GetComponent<Terrain>();
terrainData = Terrain.activeTerrain.terrainData;
}
//这是多个Perlin地形
public void MultiplePerlinTerrain()
{
float[,] heightMap = GetHeightMap();
for (int y = 0; y < terrainData.heightmapHeight; y++)
{
for (int x = 0; x < terrainData.heightmapWidth; x++)
{
//遍历表中的每组参数通过分形布朗运动,叠加将其应用于地形新高度
foreach (PerlinParameters p in perlinParameters)
{
heightMap[x, y] += Utils.fBM((x + p.mPerlinOffsetX) * p.mPerlinXScale,
(y + p.mPerlinOffsetY) * p.mPerlinYScale,
p.mPerlinOctaves,
p.mPerlinPersistance) * p.mPerlinHeightScale;
}
}
}
terrainData.SetHeights(0, 0, heightMap);
}
public void AddNewPerlin()//添加一个数据
{
perlinParameters.Add(new PerlinParameters());
}
public void RemovePerlin()//删除一个数据
{
List<PerlinParameters> keptPerlinParameters = new List<PerlinParameters>();
for (int i = 0; i < perlinParameters.Count; i++)
{
if (!perlinParameters[i].remove)
{
keptPerlinParameters.Add(perlinParameters[i]);
}
}
if (keptPerlinParameters.Count == 0) //don't want to keep any
{
keptPerlinParameters.Add(perlinParameters[0]); //add at least 1
}
perlinParameters = keptPerlinParameters;
}
}
编辑CustomTerrainEditor脚本:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using EditorGUITable;
[CustomEditor(typeof(CustomTerrain))]
public class CustomTerrainEditor : Editor {
SerializedProperty resetTerrain;
GUITableState perlinParameterTable;//表内容和关联的序列化属性放在一起
SerializedProperty perlinParameters;//具有Perlin参数的序列化属性
bool showMultiplePerlin = false;//可折叠属性面板
private void OnEnable()
{
resetTerrain = serializedObject.FindProperty("resetTerrain");
perlinParameterTable = new GUITableState("perlinParameterTable");
perlinParameters = serializedObject.FindProperty("perlinParameters");
}
//绘制编辑面板
public override void OnInspectorGUI()
{
//更新所有序列化的值
serializedObject.Update();
//获取自定义的地形属性脚本组件
CustomTerrain terrain = (CustomTerrain)target;
EditorGUILayout.PropertyField(resetTerrain);
#region
showMultiplePerlin = EditorGUILayout.Foldout(showMultiplePerlin, "Multiple Perlin Noise");
if (showMultiplePerlin)
{
EditorGUILayout.LabelField("", GUI.skin.horizontalSlider);
GUILayout.Label("Mulitple Perlin Noise", EditorStyles.boldLabel);
//相当于把所有的序列化属性放在表格里
perlinParameterTable = GUITableLayout.DrawTable(perlinParameterTable,
serializedObject.FindProperty("perlinParameters"));
GUILayout.Space(20);
EditorGUILayout.BeginHorizontal();//加减号按钮水平相邻,放在一个水平块儿中实现
if (GUILayout.Button("+"))
{
terrain.AddNewPerlin();
}
if (GUILayout.Button("-"))
{
terrain.RemovePerlin();
}
EditorGUILayout.EndHorizontal();//加减号按钮水平相邻
if (GUILayout.Button("Apply Multiple Perlin"))
{
terrain.MultiplePerlinTerrain();
}
}
#endregion
//应用发生的所有更改
serializedObject.ApplyModifiedProperties();
}
}
分形布朗运动工具类Utils:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public static class Utils {
// 分形布朗运动函数,静态方法方便从外部调用
//要在热点地图中传递一个位置x,y,
public static float fBM(float x, float y, int oct, float persistance)
{
float total = 0;
float frequency = 1;//频率是波的紧密程度,一个八度(octave)对应着频率上的加倍或减半
float amplitude = 1;//振幅
float maxValue = 0;
for (int i = 0; i < oct; i++)//遍历每个八度
{
total += Mathf.PerlinNoise(x * frequency, y * frequency) * amplitude;//通过循环叠加噪声
maxValue += amplitude;//最大值是每个oct使用的振幅相加
amplitude *= persistance;//以一定的比例降低噪声的振幅
frequency *= 2;//以一定的倍数连续升高频率
}
return total / maxValue;//注意返回值降到0到1之间的范围内
}
}