using System; using System.IO; using Unity.VisualScripting.FullSerializer; using UnityEditor; using UnityEngine; public static class GroundTools { [MenuItem("GroundTools/GroundToolWindow")] public static void OpenGroundToolWindow() { GroundToolWindow window = EditorWindow.GetWindow("地形工具"); window.Show(); } } public class GroundToolWindow : EditorWindow { private string configPath = "Assets/Editor/GroundToolConfig.asset"; private GroundToolConfig groundToolConfig; private GameObject selectedGround; private bool isConfigChange = false; private float lastChangeTime = 0;// Time.realtimeSinceStartup; private void OnEnable() { // 窗口打开时加载配置 LoadConfig(); } private void LoadConfig() { // 尝试从指定路径加载配置 groundToolConfig = AssetDatabase.LoadAssetAtPath(configPath); if (groundToolConfig == null) { // 如果配置不存在,则创建一个新的 groundToolConfig = CreateInstance(); // 确保目录存在 string directory = Path.GetDirectoryName(configPath); if (!Directory.Exists(directory)) { Directory.CreateDirectory(directory); } // 创建资产文件 AssetDatabase.CreateAsset(groundToolConfig, configPath); AssetDatabase.SaveAssets(); Debug.Log("创建新的地形工具配置文件: " + configPath); } } private void SaveConfig() { if (groundToolConfig != null) { EditorUtility.SetDirty(groundToolConfig); // 确保Unity知道资产需要保存 AssetDatabase.SaveAssets(); // 保存资产 isConfigChange = false; Debug.Log("地形工具配置已保存。"); } } /// /// 绘制编辑器窗口UI /// private void OnGUI() { GUILayout.Label("地形创建工具", EditorStyles.boldLabel); // 创建地形按钮 if (GUILayout.Button("创建地形")) { //选择需要存储文件的路径 CreateGround(); } EditorGUILayout.Space(10); // 参数设置 groundToolConfig.width = EditorGUILayout.IntField("横格", groundToolConfig.width); groundToolConfig.length = EditorGUILayout.IntField("纵格", groundToolConfig.length); groundToolConfig.height = EditorGUILayout.FloatField("高度", groundToolConfig.height); groundToolConfig.perlinScale = EditorGUILayout.Slider("噪声缩放", groundToolConfig.perlinScale, 0.01f, 1f); groundToolConfig.groundIncline = EditorGUILayout.Slider("地面坡度", groundToolConfig.groundIncline, 0f, 1f); EditorGUILayout.Space(10); groundToolConfig.power = EditorGUILayout.Slider("过渡平滑度", groundToolConfig.power, 1f, 5f); groundToolConfig.matSplit1 = EditorGUILayout.Slider("纹理占比分割1", groundToolConfig.matSplit1, 0f, 0.8f); groundToolConfig.matSplit2 = EditorGUILayout.Slider("纹理占比分割2", groundToolConfig.matSplit2, 0.1f, 0.9f); groundToolConfig.matSplit3 = EditorGUILayout.Slider("纹理占比分割3", groundToolConfig.matSplit3, 0.2f, 1f); EditorGUILayout.Space(10); groundToolConfig.texGround1 = EditorGUILayout.ObjectField("纹理1", groundToolConfig.texGround1, typeof(Texture), false) as Texture; groundToolConfig.texGround2 = EditorGUILayout.ObjectField("纹理2", groundToolConfig.texGround2, typeof(Texture), false) as Texture; groundToolConfig.texGround3 = EditorGUILayout.ObjectField("纹理3", groundToolConfig.texGround3, typeof(Texture), false) as Texture; groundToolConfig.texGround4 = EditorGUILayout.ObjectField("纹理4", groundToolConfig.texGround4, typeof(Texture), false) as Texture; EditorGUILayout.Space(10); if (GUILayout.Button("随机参数")) { UnityEngine.Random.InitState((int)DateTime.Now.Ticks); groundToolConfig.randOffset = UnityEngine.Random.Range(0f, 100f); } groundToolConfig.randOffset = EditorGUILayout.FloatField("随机性", groundToolConfig.randOffset); groundToolConfig.randJitter = EditorGUILayout.Slider("纹理扰动", groundToolConfig.randJitter,0, 1); EditorGUILayout.Space(10); EditorGUILayout.Space(10); // 选择地形对象 selectedGround = EditorGUILayout.ObjectField("选择地形", selectedGround, typeof(GameObject), true) as GameObject; GUI.enabled = selectedGround != null; // 修改地形按钮 if (GUILayout.Button("修改地形")) { ChangeGround(); } if (GUI.changed) { isConfigChange = true; lastChangeTime = Time.realtimeSinceStartup; } if (isConfigChange && Time.realtimeSinceStartup - lastChangeTime > 5f) { SaveConfig(); } GUI.enabled = true; } /// /// 创建地形 /// private void CreateGround() { // 创建游戏对象 GameObject ground = new GameObject("Ground_" + groundToolConfig.width + "x" + groundToolConfig.length); // 添加网格过滤器和网格渲染器 MeshFilter meshFilter = ground.AddComponent(); MeshRenderer meshRenderer = ground.AddComponent(); // 创建网格 Mesh mesh = CreateGroundMesh(groundToolConfig.width, groundToolConfig.length, groundToolConfig.height, groundToolConfig.perlinScale); // 应用网格 meshFilter.sharedMesh = mesh; // 添加碰撞器 MeshCollider collider = ground.AddComponent(); collider.sharedMesh = mesh; // 设置材质 meshRenderer.material = new Material(Shader.Find("Standard")); // 注册撤销操作并选中对象 Undo.RegisterCreatedObjectUndo(ground, "Create Ground"); Selection.activeGameObject = ground; // 更新选中的地形对象 selectedGround = ground; } /// /// 修改地形 /// private void ChangeGround() { if (selectedGround == null) { Debug.LogError("请先选择一个地形对象"); return; } MeshFilter meshFilter = selectedGround.GetComponent(); if (meshFilter == null) return; // 创建新网格 Mesh mesh = meshFilter.sharedMesh;//CreateGroundMesh(width, length, height, perlinScale); // 记录撤销 Undo.RecordObject(meshFilter, "Change Ground"); // 应用网格 meshFilter.sharedMesh = mesh; float[,] heightMap = ModifyVertexHeight(mesh); ModifyMaterial(selectedGround.GetComponent(), heightMap); // 更新碰撞器 MeshCollider collider = selectedGround.GetComponent(); if (collider != null) { Undo.RecordObject(collider, "Change Ground Collider"); collider.sharedMesh = mesh; } } /// /// 创建地形网格 /// private Mesh CreateGroundMesh(int width, int length, float height, float perlinScale) { Mesh mesh = new Mesh(); mesh.name = "Ground_" + width + "x" + length + "_Mesh"; int resolutionX = Mathf.CeilToInt(groundToolConfig.width); int resolutionZ = Mathf.CeilToInt(groundToolConfig.length); float halfWidth = width * 0.5f; float halfLength = length * 0.5f; // 创建顶点 Vector3[] vertices = new Vector3[(resolutionX + 1) * (resolutionZ + 1)]; float[,] heightMap = GenerateHeightMap(groundToolConfig.width, groundToolConfig.length, groundToolConfig.height, groundToolConfig.perlinScale); for (int z = 0; z <= resolutionZ; z++) { for (int x = 0; x <= resolutionX; x++) { float xPos = x;// * width / resolutionX - halfWidth; float zPos = z;// * length / resolutionZ - halfLength; // 使用Perlin噪声生成高度 float y = 0; if (perlinScale > 0) { y = heightMap[x, z]; } vertices[z * (resolutionX + 1) + x] = new Vector3(xPos, y, zPos); } } // 创建三角形 int[] triangles = new int[resolutionX * resolutionZ * 6]; int triangleIndex = 0; for (int z = 0; z < resolutionZ; z++) { for (int x = 0; x < resolutionX; x++) { int vertexIndex = z * (resolutionX + 1) + x; triangles[triangleIndex++] = vertexIndex; triangles[triangleIndex++] = vertexIndex + resolutionX + 1; triangles[triangleIndex++] = vertexIndex + 1; triangles[triangleIndex++] = vertexIndex + 1; triangles[triangleIndex++] = vertexIndex + resolutionX + 1; triangles[triangleIndex++] = vertexIndex + resolutionX + 2; } } // 创建UV坐标 Vector2[] uvs = new Vector2[(resolutionX + 1) * (resolutionZ + 1)]; for (int z = 0; z <= resolutionZ; z++) { for (int x = 0; x <= resolutionX; x++) { uvs[z * (resolutionX + 1) + x] = new Vector2((float)x / resolutionX, (float)z / resolutionZ); } } // 设置网格数据 mesh.vertices = vertices; mesh.triangles = triangles; mesh.uv = uvs; mesh.RecalculateNormals(); mesh.RecalculateBounds(); return mesh; } //修改模型顶点高度 private float[,] ModifyVertexHeight(Mesh mesh) { float[,] heightMap = GenerateHeightMap(groundToolConfig.width, groundToolConfig.length, groundToolConfig.height, groundToolConfig.perlinScale); //for (int i = 0; i < vertices.Length; i++) //{ // vertices[i].y = heightMap[i % (width + 1), i / (width + 1)]; //} int resolutionX = Mathf.CeilToInt(groundToolConfig.width); int resolutionZ = Mathf.CeilToInt(groundToolConfig.length); Vector3[] vertices = new Vector3[(resolutionX + 1) * (resolutionZ + 1)]; for (int z = 0; z <= resolutionZ; z++) { for (int x = 0; x <= resolutionX; x++) { float xPos = x;// * width / resolutionX - halfWidth; float zPos = z;// * length / resolutionZ - halfLength; // 使用Perlin噪声生成高度 float y = 0; if (groundToolConfig.perlinScale > 0) { y = heightMap[x, z]; } vertices[z * (resolutionX + 1) + x] = new Vector3(xPos, y, zPos); } } // 创建三角形 int[] triangles = new int[resolutionX * resolutionZ * 6]; int triangleIndex = 0; for (int z = 0; z < resolutionZ; z++) { for (int x = 0; x < resolutionX; x++) { int vertexIndex = z * (resolutionX + 1) + x; triangles[triangleIndex++] = vertexIndex; triangles[triangleIndex++] = vertexIndex + resolutionX + 1; triangles[triangleIndex++] = vertexIndex + 1; triangles[triangleIndex++] = vertexIndex + 1; triangles[triangleIndex++] = vertexIndex + resolutionX + 1; triangles[triangleIndex++] = vertexIndex + resolutionX + 2; } } // 创建UV坐标 Vector2[] uvs = new Vector2[(resolutionX + 1) * (resolutionZ + 1)]; for (int z = 0; z <= resolutionZ; z++) { for (int x = 0; x <= resolutionX; x++) { uvs[z * (resolutionX + 1) + x] = new Vector2((float)x / resolutionX, (float)z / resolutionZ); } } mesh.vertices = vertices; mesh.triangles = triangles; mesh.uv = uvs; mesh.RecalculateNormals(); mesh.RecalculateBounds(); return heightMap; } //填入材质 private void ModifyMaterial(MeshRenderer meshRenderer, float[,] heightMap) { // 设置材质 Material mtl = new Material(Shader.Find("XWorld/XTerrain")); //设置_LayerTex0 _LayerTex1 _LayerTex2 _LayerTex3 和_Control贴图 mtl.SetTexture("_LayerTex3", groundToolConfig.texGround4); mtl.SetTexture("_LayerTex2", groundToolConfig.texGround3); mtl.SetTexture("_LayerTex1", groundToolConfig.texGround2); mtl.SetTexture("_LayerTex0", groundToolConfig.texGround1); //生成一个rgba四通道的控制贴图,随机分配四种材质的混合度 Texture2D controlTex = new Texture2D(groundToolConfig.width, groundToolConfig.length, TextureFormat.RGBA32, false); for (int z = 0; z <= groundToolConfig.width; z++) { for (int x = 0; x <= groundToolConfig.length; x++) { //float h1 = heightMap[x, z]/height; float h1 = Mathf.PerlinNoise(groundToolConfig.randOffset + z * groundToolConfig.perlinScale, groundToolConfig.randOffset + x * groundToolConfig.perlinScale); float h2 = Mathf.PerlinNoise(groundToolConfig.randOffset + 107 + z * groundToolConfig.perlinScale, groundToolConfig.randOffset + 107 + x * groundToolConfig.perlinScale); Color coefficients = new Color(0, 0, 0, 0); float perlinValue = h1 * (1 - groundToolConfig.randJitter) + h2 * groundToolConfig.randJitter; float p; float Split12 = (groundToolConfig.matSplit1 + groundToolConfig.matSplit2)/2.0f; float Split23 = (groundToolConfig.matSplit3 + groundToolConfig.matSplit2) / 2.0f; ; // 确定主导纹理区间 if (perlinValue < Split12)//0.33f)// { if (perlinValue < groundToolConfig.matSplit1) { p = 0.5f - Mathf.Pow((groundToolConfig.matSplit1 - perlinValue) / groundToolConfig.matSplit1, groundToolConfig.power)*0.5f; // 纹理0主导:R通道为主,平滑过渡到纹理1 coefficients.r = 1.0f - p; coefficients.g = p; } else { p = 0.5f + Mathf.Pow((perlinValue - groundToolConfig.matSplit1) / (Split12 - groundToolConfig.matSplit1), groundToolConfig.power)*0.5f; coefficients.r = 1.0f - p; coefficients.g = p; } //p = Mathf.Pow(perlinValue / groundToolConfig.matSplit1, groundToolConfig.power); //// 纹理1主导:R通道为主,平滑过渡到纹理2 //coefficients.r = 1.0f - p; //coefficients.g = p; } else if (perlinValue < Split23)//0.66f)// { if(perlinValue < groundToolConfig.matSplit2) { p = 0.5f - Mathf.Pow(((groundToolConfig.matSplit2 - perlinValue) / (groundToolConfig.matSplit2 - Split12)), groundToolConfig.power)*0.5f; } else { p = 0.5f + Mathf.Pow(((perlinValue - groundToolConfig.matSplit2) / (Split23 - groundToolConfig.matSplit2)), groundToolConfig.power)*0.5f; } coefficients.g = 1.0f - p; coefficients.b = p; //p = Mathf.Pow(((perlinValue - groundToolConfig.matSplit1) / (groundToolConfig.matSplit2 - groundToolConfig.matSplit1)), groundToolConfig.power); //// 纹理2主导:G通道为主,平滑过渡到相邻纹理 //coefficients.g = 1.0f - p; //coefficients.b = p; } else { if(perlinValue < groundToolConfig.matSplit3) { p = 0.5f - Mathf.Pow(((groundToolConfig.matSplit3 - perlinValue) / (groundToolConfig.matSplit3 - Split23)), groundToolConfig.power)*0.5f; } else { p = 0.5f + Mathf.Pow(((perlinValue - groundToolConfig.matSplit3) / (1.0f - groundToolConfig.matSplit3)), groundToolConfig.power)*0.5f; } coefficients.b = 1.0f - p; coefficients.a = p; //p = Mathf.Pow(((perlinValue - groundToolConfig.matSplit2) / (1.0f - groundToolConfig.matSplit2)), groundToolConfig.power); //// 纹理3主导:B通道为主 //coefficients.b = 1.0f - p; //coefficients.a = p; } controlTex.SetPixel(z, x, coefficients); } } controlTex.Apply(); mtl.SetTexture("_Control", controlTex); meshRenderer.material = mtl; } public float[,] GenerateHeightMap(int x, int z, float heightScale = 1f, float PerlinScale = 0.01f) { float[,] heightMap = new float[x + 1, z + 1]; //生成一个N分之一宽和高的高度图 int N = 16; int quarterX = x / N; int quarterZ = z / N; float[,] quarterHeightMap = new float[quarterX + 1, quarterZ + 1]; //for (int i = 0; i <= quarterX; i++) //{ // for (int j = 0; j <= quarterZ; j++) // { // quarterHeightMap[i, j] = Mathf.PerlinNoise(randOffset + i * 0.1f, randOffset + j * 0.1f) * heightScale*2; // } //} //将四分之一高度图叠加到总的高度图上 int f33 = 0, f66 = 0, f100 = 0; for (int i = 0; i <= x; i++) { for (int j = 0; j <= z; j++) { //heightMap[i, j] = Mathf.PerlinNoise(i * 0.1f, j * 0.1f) * heightScale + quarterHeightMap[i / N, j / N]; //heightMap[i, j] = quarterHeightMap[i / N, j / N]; float h1 = Mathf.PerlinNoise(groundToolConfig.randOffset + i * PerlinScale, groundToolConfig.randOffset + j * PerlinScale) * heightScale; float h2 = Mathf.PerlinNoise(groundToolConfig.randOffset + i * PerlinScale * groundToolConfig.groundIncline, groundToolConfig.randOffset + j * PerlinScale * groundToolConfig.groundIncline) * heightScale; heightMap[i, j] = h1 > h2? h1 : h2; if (h2 < 0.33f) { f33++; } else if (h2 < 0.66f) { f66++; } else if (h2 <= 1.0f) { f100++; } } } Debug.Log($"TotalCount: {f33}, {f66}, {f100}"); return heightMap; } }