Initial commit: Client Doc docs Server Tools
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,478 @@
|
||||
|
||||
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<GroundToolWindow>("地形工具");
|
||||
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<GroundToolConfig>(configPath);
|
||||
if (groundToolConfig == null)
|
||||
{
|
||||
// 如果配置不存在,则创建一个新的
|
||||
groundToolConfig = CreateInstance<GroundToolConfig>();
|
||||
// 确保目录存在
|
||||
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("地形工具配置已保存。");
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// 绘制编辑器窗口UI
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建地形
|
||||
/// </summary>
|
||||
private void CreateGround()
|
||||
{
|
||||
// 创建游戏对象
|
||||
GameObject ground = new GameObject("Ground_" + groundToolConfig.width + "x" + groundToolConfig.length);
|
||||
|
||||
// 添加网格过滤器和网格渲染器
|
||||
MeshFilter meshFilter = ground.AddComponent<MeshFilter>();
|
||||
MeshRenderer meshRenderer = ground.AddComponent<MeshRenderer>();
|
||||
|
||||
// 创建网格
|
||||
Mesh mesh = CreateGroundMesh(groundToolConfig.width, groundToolConfig.length, groundToolConfig.height, groundToolConfig.perlinScale);
|
||||
|
||||
// 应用网格
|
||||
meshFilter.sharedMesh = mesh;
|
||||
|
||||
// 添加碰撞器
|
||||
MeshCollider collider = ground.AddComponent<MeshCollider>();
|
||||
collider.sharedMesh = mesh;
|
||||
|
||||
// 设置材质
|
||||
meshRenderer.material = new Material(Shader.Find("Standard"));
|
||||
|
||||
// 注册撤销操作并选中对象
|
||||
Undo.RegisterCreatedObjectUndo(ground, "Create Ground");
|
||||
Selection.activeGameObject = ground;
|
||||
|
||||
// 更新选中的地形对象
|
||||
selectedGround = ground;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 修改地形
|
||||
/// </summary>
|
||||
private void ChangeGround()
|
||||
{
|
||||
if (selectedGround == null)
|
||||
{
|
||||
Debug.LogError("请先选择一个地形对象");
|
||||
return;
|
||||
}
|
||||
|
||||
MeshFilter meshFilter = selectedGround.GetComponent<MeshFilter>();
|
||||
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<MeshRenderer>(), heightMap);
|
||||
|
||||
// 更新碰撞器
|
||||
MeshCollider collider = selectedGround.GetComponent<MeshCollider>();
|
||||
if (collider != null)
|
||||
{
|
||||
Undo.RecordObject(collider, "Change Ground Collider");
|
||||
collider.sharedMesh = mesh;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建地形网格
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ece4136e36bcf5048b2dc0cc5b8f5a09
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,177 @@
|
||||
using System;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
public static class GroundCreate
|
||||
{
|
||||
//[MenuItem("GroundTools/GroundCreate")]
|
||||
/// <summary>
|
||||
/// 创建一个100x100单位的地表模型
|
||||
/// </summary>
|
||||
public static void CreateGround()
|
||||
{
|
||||
|
||||
// 设置网格参数
|
||||
int width = 128;
|
||||
int length = 128;
|
||||
|
||||
// 创建游戏对象
|
||||
GameObject ground = new GameObject($"Ground");
|
||||
|
||||
// 添加网格过滤器和网格渲染器
|
||||
MeshFilter meshFilter = ground.AddComponent<MeshFilter>();
|
||||
MeshRenderer meshRenderer = ground.AddComponent<MeshRenderer>();
|
||||
|
||||
UnityEngine.Random.InitState((int)DateTime.Now.Ticks);
|
||||
float randOffset = UnityEngine.Random.Range(0f, 100f);
|
||||
|
||||
// 创建网格
|
||||
Mesh mesh = new Mesh();
|
||||
mesh.name = $"Ground_{width}X{length}_Mesh";
|
||||
|
||||
float halfWidth = width * 0.5f;
|
||||
float halfLength = length * 0.5f;
|
||||
|
||||
//创建一个随机高度图,并填入到顶点的y上
|
||||
float[,] heightMap = GenerateHeightMap(width, length);
|
||||
//for (int z = 0; z <= length; z++)
|
||||
//{
|
||||
// for (int x = 0; x <= width; x++)
|
||||
// {
|
||||
// heightMap[x, z] = Mathf.PerlinNoise(x * 0.1f, z * 0.1f) * 2f - 1f;
|
||||
// }
|
||||
//}
|
||||
// 创建顶点
|
||||
Vector3[] vertices = new Vector3[(width + 1) * (length + 1)];
|
||||
for (int z = 0; z <= length; z++)
|
||||
{
|
||||
for (int x = 0; x <= width; x++)
|
||||
{
|
||||
vertices[z * (width + 1) + x] = new Vector3(x - halfWidth, heightMap[x, z], z - halfLength);
|
||||
}
|
||||
}
|
||||
|
||||
// 创建三角形
|
||||
int[] triangles = new int[width * length * 6];
|
||||
int triangleIndex = 0;
|
||||
for (int z = 0; z < length; z++)
|
||||
{
|
||||
for (int x = 0; x < width; x++)
|
||||
{
|
||||
int vertexIndex = z * (width + 1) + x;
|
||||
|
||||
triangles[triangleIndex++] = vertexIndex;
|
||||
triangles[triangleIndex++] = vertexIndex + width + 1;
|
||||
triangles[triangleIndex++] = vertexIndex + 1;
|
||||
|
||||
triangles[triangleIndex++] = vertexIndex + 1;
|
||||
triangles[triangleIndex++] = vertexIndex + width + 1;
|
||||
triangles[triangleIndex++] = vertexIndex + width + 2;
|
||||
}
|
||||
}
|
||||
|
||||
// 创建UV坐标
|
||||
Vector2[] uvs = new Vector2[(width + 1) * (length + 1)];
|
||||
for (int z = 0; z <= length; z++)
|
||||
{
|
||||
for (int x = 0; x <= width; x++)
|
||||
{
|
||||
uvs[z * (width + 1) + x] = new Vector2((float)x / width, (float)z / length);
|
||||
}
|
||||
}
|
||||
|
||||
// 设置网格数据
|
||||
mesh.vertices = vertices;
|
||||
mesh.triangles = triangles;
|
||||
mesh.uv = uvs;
|
||||
mesh.RecalculateNormals();
|
||||
mesh.RecalculateBounds();
|
||||
|
||||
// 应用网格
|
||||
meshFilter.sharedMesh = mesh;
|
||||
|
||||
// 添加碰撞器
|
||||
MeshCollider collider = ground.AddComponent<MeshCollider>();
|
||||
collider.sharedMesh = mesh;
|
||||
|
||||
// 设置材质
|
||||
meshRenderer.material = new Material(Shader.Find("XWorld/XTerrain"));
|
||||
//设置_LayerTex0 _LayerTex1 _LayerTex2 _LayerTex3 和_Control贴图
|
||||
meshRenderer.material.SetTexture("_LayerTex0", AssetDatabase.LoadAssetAtPath<Texture2D>("Assets/Arts/scene/Terrain/Texture/grass.png"));
|
||||
meshRenderer.material.SetTexture("_LayerTex1", AssetDatabase.LoadAssetAtPath<Texture2D>("Assets/Arts/scene/Terrain/Texture/blick.png"));
|
||||
meshRenderer.material.SetTexture("_LayerTex2", AssetDatabase.LoadAssetAtPath<Texture2D>("Assets/Arts/scene/Terrain/Texture/mud.png"));
|
||||
meshRenderer.material.SetTexture("_LayerTex3", AssetDatabase.LoadAssetAtPath<Texture2D>("Assets/Arts/scene/Terrain/Texture/sand.png"));
|
||||
//生成一个rgba四通道的控制贴图,随机分配四种材质的混合度
|
||||
Texture2D controlTex = new Texture2D(width, length, TextureFormat.RGBA32, false);
|
||||
float PerlinRandom = 0.05f;
|
||||
for (int z = 0; z < length; z++)
|
||||
{
|
||||
for (int x = 0; x < width; x++)
|
||||
{
|
||||
//float h = heightMap[x, z];
|
||||
float h1 = Mathf.PerlinNoise(randOffset + z * PerlinRandom, randOffset + x * PerlinRandom);
|
||||
float hLerp;
|
||||
Color color;// = new Color(0, 0, 0, 0);
|
||||
if (h1 < 0.33f)
|
||||
{
|
||||
hLerp = (0.33f - h1)*3;
|
||||
color = new Color(0, 0, hLerp, 1 - hLerp);
|
||||
}
|
||||
else if (h1 < 0.66f)
|
||||
{
|
||||
hLerp = (0.66f - h1) * 3;
|
||||
color = new Color(0, hLerp, 1- hLerp, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
hLerp = (1.0f - h1) * 3;
|
||||
color = new Color(hLerp, 1 - hLerp, 0, 0);
|
||||
}
|
||||
controlTex.SetPixel(x, z, color);
|
||||
}
|
||||
}
|
||||
controlTex.Apply();
|
||||
meshRenderer.material.SetTexture("_Control", controlTex);
|
||||
|
||||
|
||||
// 注册撤销操作并选中对象
|
||||
Undo.RegisterCreatedObjectUndo(ground, "Create 100x100 Ground");
|
||||
Selection.activeGameObject = ground;
|
||||
}
|
||||
|
||||
//生成x * z的高度图,增加一个浮动变化的高度和一个不均匀的高度变化
|
||||
//heightScale 高度缩放因子
|
||||
//heightOffset 高度偏移量
|
||||
public static float[,] GenerateHeightMap(int x, int z, float heightScale = 20f, float PerlinRandom = 0.01f)
|
||||
{
|
||||
float[,] heightMap = new float[x + 1, z + 1];
|
||||
UnityEngine.Random.InitState(Time.frameCount);
|
||||
Debug.Log("Random Seed: " + Time.frameCount);
|
||||
//生成一个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(i * 0.1f, j * 0.1f) * heightScale*2;
|
||||
}
|
||||
}
|
||||
//将四分之一高度图叠加到总的高度图上
|
||||
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(i * PerlinRandom, j * PerlinRandom) * heightScale;
|
||||
float h2 = Mathf.PerlinNoise(i * PerlinRandom*5, j * PerlinRandom*5) * heightScale;
|
||||
heightMap[i, j] = h1 > h2? h1 : h2;
|
||||
}
|
||||
}
|
||||
return heightMap;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ff4bce06a097bd4489beb1dc676d328c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,29 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!114 &11400000
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 0}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: e6914217da6edb34baad8872f8642df3, type: 3}
|
||||
m_Name: GroundToolConfig
|
||||
m_EditorClassIdentifier:
|
||||
randOffset: 24.258888
|
||||
randJitter: 0.382
|
||||
width: 256
|
||||
length: 256
|
||||
height: 30
|
||||
perlinScale: 0.05
|
||||
groundIncline: 0
|
||||
matSplit1: 0.088
|
||||
matSplit2: 0.285
|
||||
matSplit3: 0.593
|
||||
power: 4.12
|
||||
texGround1: {fileID: 2800000, guid: 57d24bd9d5619994fb7856632f627ea5, type: 3}
|
||||
texGround2: {fileID: 2800000, guid: d6d26791584699a4ca75b0c5b9f173f2, type: 3}
|
||||
texGround3: {fileID: 2800000, guid: 741c6de7ade0623478730047979b094b, type: 3}
|
||||
texGround4: {fileID: 2800000, guid: 4d4a4a42f2189504b8bd44c09ace0136, type: 3}
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ddbf17bfa8fc8fd4eab0bf98d05492c7
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 11400000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,25 @@
|
||||
|
||||
using UnityEngine;
|
||||
|
||||
// 创建菜单项,方便在项目中创建该资产
|
||||
[CreateAssetMenu(fileName = "GroundToolConfig", menuName = "GroundTools/Ground Tool Configuration")]
|
||||
public class GroundToolConfig : ScriptableObject
|
||||
{
|
||||
// 将所有需要保存的参数定义为public字段或[SerializeField]的私有字段
|
||||
public float randOffset = 0f;
|
||||
public float randJitter = 0.1f;// 随机扰动
|
||||
public int width = 128;
|
||||
public int length = 128;
|
||||
public float height = 30f;
|
||||
public float perlinScale = 0.05f;
|
||||
public float groundIncline = 0.01f;
|
||||
public float matSplit1 = 0.25f;
|
||||
public float matSplit2 = 0.5f;
|
||||
public float matSplit3 = 0.75f;
|
||||
public float power = 2f;
|
||||
// 纹理(Texture)是Unity引擎对象的引用,本身可以被序列化
|
||||
public Texture texGround1;
|
||||
public Texture texGround2;
|
||||
public Texture texGround3;
|
||||
public Texture texGround4;
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e6914217da6edb34baad8872f8642df3
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user