/
tracie
/
CodeGIAS
Обзор
Документация
Войти
/
tracie
/
CodeGIAS
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
Assets/Terrain/Scripts/TerrainGenerator.cs
396 строк
13 KB
tracie
AllReworked
01 окт 2025, 19:12
01 окт 2025, 19:12
b02ae52
Код
Авторство
О чём код?
using UnityEngine; using UnityEditor; using System.Collections.Generic; public class SimpleTerrainGenerator : MonoBehaviour { [Header("Settings")] public SimpleTerrainSettings settings; [Header("Target Terrain")] public Terrain targetTerrain; [ContextMenu("Generate Simple Terrain")] public void GenerateSimpleTerrain() { if (targetTerrain == null) { Debug.LogError("Target Terrain не назначен!"); return; } if (settings == null) { Debug.LogError("Settings не назначены!"); return; } if (settings.grassLayer == null || settings.pathLayer == null || settings.gravelLayer == null) { Debug.LogError("Не все Terrain Layers назначены! Проверьте Settings → Terrain Layers в Inspector"); return; } SetupTerrainLayers(); GenerateSimpleHeights(); GenerateSimpleTextures(); PlaceSimpleVegetation(); Debug.Log("Простая генерация террейна завершена!"); } // НОВЫЙ ПУБЛИЧНЫЙ МЕТОД для тестирования травы [ContextMenu("Test Grass Only")] public void TestGrassOnly() { if (targetTerrain == null) { Debug.LogError("Target Terrain не назначен!"); return; } if (settings == null || settings.grassPrefabs == null || settings.grassPrefabs.Length == 0) { Debug.LogError("Префабы травы не назначены!"); return; } PlaceGrassDetails(); Debug.Log("Только трава размещена для тестирования"); } private void SetupTerrainLayers() { TerrainLayer[] layers = new TerrainLayer[] { settings.grassLayer, settings.pathLayer, settings.gravelLayer }; targetTerrain.terrainData.terrainLayers = layers; } private void GenerateSimpleHeights() { TerrainData terrainData = targetTerrain.terrainData; int width = terrainData.heightmapResolution; int height = terrainData.heightmapResolution; float[,] heights = new float[width, height]; for (int x = 0; x < width; x++) { for (int y = 0; y < height; y++) { float xCoord = (float)x / width * 5f; float yCoord = (float)y / height * 5f; float noiseValue = Mathf.PerlinNoise(xCoord, yCoord); heights[x, y] = noiseValue * (settings.terrainHeight / terrainData.size.y); } } terrainData.SetHeights(0, 0, heights); } private void GenerateSimpleTextures() { TerrainData terrainData = targetTerrain.terrainData; int width = terrainData.alphamapWidth; int height = terrainData.alphamapHeight; float[,,] alphaMap = new float[width, height, 3]; float offsetX = Random.Range(0f, 1000f); float offsetY = Random.Range(0f, 1000f); for (int x = 0; x < width; x++) { for (int y = 0; y < height; y++) { float noise = Mathf.PerlinNoise( (x * 0.01f) + offsetX, (y * 0.01f) + offsetY ); if (noise < 0.3f) { alphaMap[x, y, 1] = 1f; // Path } else if (noise < 0.6f) { alphaMap[x, y, 0] = 1f; // Grass } else { alphaMap[x, y, 2] = 1f; // Gravel } } } terrainData.SetAlphamaps(0, 0, alphaMap); } private void PlaceSimpleVegetation() { if (settings.treePrefabs != null && settings.treePrefabs.Length > 0) { PlaceTrees(); } if (settings.grassPrefabs != null && settings.grassPrefabs.Length > 0) { PlaceGrassDetails(); } } private void PlaceTrees() { TerrainData terrainData = targetTerrain.terrainData; List<TreePrototype> treePrototypes = new List<TreePrototype>(); foreach (GameObject treePrefab in settings.treePrefabs) { if (treePrefab != null) { treePrototypes.Add(new TreePrototype { prefab = treePrefab }); } } if (treePrototypes.Count == 0) { Debug.LogWarning("Нет префабов деревьев!"); return; } terrainData.treePrototypes = treePrototypes.ToArray(); List<TreeInstance> treeInstances = new List<TreeInstance>(); float[,,] alphamaps = terrainData.GetAlphamaps(0, 0, terrainData.alphamapWidth, terrainData.alphamapHeight); float noiseOffsetX = Random.Range(0f, 1000f); float noiseOffsetY = Random.Range(0f, 1000f); for (int i = 0; i < settings.treeCount; i++) { float normalizedX = Random.Range(0f, 1f); float normalizedZ = Random.Range(0f, 1f); int mapX = Mathf.FloorToInt(normalizedX * (terrainData.alphamapWidth - 1)); int mapZ = Mathf.FloorToInt(normalizedZ * (terrainData.alphamapHeight - 1)); // Проверяем, что в этом месте текстура травы if (alphamaps[mapX, mapZ, 0] > 0.5f) { float noise = Mathf.PerlinNoise(normalizedX * 10f + noiseOffsetX, normalizedZ * 10f + noiseOffsetY); if (noise > 0.5f) // регулируем плотность деревьев { float terrainHeight = targetTerrain.SampleHeight(new Vector3( normalizedX * terrainData.size.x, 0, normalizedZ * terrainData.size.z )) / terrainData.size.y; TreeInstance tree = new TreeInstance { position = new Vector3(normalizedX, terrainHeight, normalizedZ), prototypeIndex = Random.Range(0, treePrototypes.Count), widthScale = Random.Range(0.8f, 1.2f), heightScale = Random.Range(0.8f, 1.3f), color = Color.white, lightmapColor = Color.white }; treeInstances.Add(tree); } } } terrainData.treeInstances = treeInstances.ToArray(); Debug.Log($"Размещено {treeInstances.Count} деревьев"); } private void PlaceGrassDetails() { TerrainData terrainData = targetTerrain.terrainData; // Очистка старых прототипов terrainData.detailPrototypes = new DetailPrototype[0]; // Подготовка префабов травы List<DetailPrototype> detailPrototypes = new List<DetailPrototype>(); foreach (GameObject grassPrefab in settings.grassPrefabs) { if (grassPrefab != null) { DetailPrototype prototype = new DetailPrototype { prototype = grassPrefab, renderMode = DetailRenderMode.VertexLit, usePrototypeMesh = true, healthyColor = new Color(0.6f, 0.9f, 0.3f, 1f), dryColor = new Color(0.8f, 0.7f, 0.4f, 1f), minWidth = 0.3f, maxWidth = 0.8f, minHeight = 0.3f, maxHeight = 0.8f, noiseSpread = 0.2f }; detailPrototypes.Add(prototype); } } if (detailPrototypes.Count == 0) { Debug.LogWarning("Нет префабов травы!"); return; } terrainData.detailPrototypes = detailPrototypes.ToArray(); int detailWidth = terrainData.detailWidth; int detailHeight = terrainData.detailHeight; float[,,] alphamaps = terrainData.GetAlphamaps(0, 0, terrainData.alphamapWidth, terrainData.alphamapHeight); // Рандомное смещение для шума float noiseOffsetX = Random.Range(0f, 1000f); float noiseOffsetY = Random.Range(0f, 1000f); for (int prototypeIndex = 0; prototypeIndex < detailPrototypes.Count; prototypeIndex++) { int[,] detailMap = new int[detailWidth, detailHeight]; int grassPlaced = 0; for (int x = 0; x < detailWidth; x++) { for (int y = 0; y < detailHeight; y++) { // Приводим координаты detail -> alphaMap int mapX = Mathf.FloorToInt((float)x / detailWidth * terrainData.alphamapWidth); int mapY = Mathf.FloorToInt((float)y / detailHeight * terrainData.alphamapHeight); // Только если здесь трава (layer 0) if (alphamaps[mapX, mapY, 0] > 0.5f) { float noise = Mathf.PerlinNoise(x * 0.1f + noiseOffsetX, y * 0.1f + noiseOffsetY); // Контроль плотности через шум if (noise > 0.4f) { detailMap[x, y] = Random.Range(1, 4); // 1–3 травинки grassPlaced++; } } } } terrainData.SetDetailLayer(0, 0, prototypeIndex, detailMap); Debug.Log($"Трава: размещено {grassPlaced} точек для прототипа {prototypeIndex}"); } } } [System.Serializable] public class SimpleTerrainSettings { [Header("Terrain Layers")] public TerrainLayer grassLayer; public TerrainLayer pathLayer; public TerrainLayer gravelLayer; [Header("Tree Prefabs")] public GameObject[] treePrefabs; [Header("Grass Prefabs")] public GameObject[] grassPrefabs; [Header("Generation Settings")] [Range(0, 200)] public int treeCount = 50; [Range(0, 500)] public int grassCount = 200; public float terrainSize = 100f; public float terrainHeight = 10f; } #if UNITY_EDITOR [CustomEditor(typeof(SimpleTerrainGenerator))] public class SimpleTerrainGeneratorEditor : Editor { public override void OnInspectorGUI() { DrawDefaultInspector(); GUILayout.Space(10); SimpleTerrainGenerator generator = (SimpleTerrainGenerator)target; // Проверка настроек if (generator.settings == null) { EditorGUILayout.HelpBox("Settings не назначены!", MessageType.Error); } else if (generator.settings.grassLayer == null || generator.settings.pathLayer == null || generator.settings.gravelLayer == null) { EditorGUILayout.HelpBox("Не все Terrain Layers назначены!", MessageType.Warning); } if (generator.targetTerrain == null) { EditorGUILayout.HelpBox("Target Terrain не назначен!", MessageType.Error); } // Отображение информации о траве if (generator.settings != null && generator.settings.grassPrefabs != null) { EditorGUILayout.HelpBox($"Назначено {generator.settings.grassPrefabs.Length} префабов травы", MessageType.Info); } if (GUILayout.Button("Generate Simple Terrain", GUILayout.Height(30))) { generator.GenerateSimpleTerrain(); } // ИСПРАВЛЕНО: Используем публичный метод if (GUILayout.Button("Test Grass Only", GUILayout.Height(25))) { generator.TestGrassOnly(); } if (GUILayout.Button("Clear Terrain")) { if (generator.targetTerrain != null && generator.targetTerrain.terrainData != null) { TerrainData data = generator.targetTerrain.terrainData; // Очищаем высоты float[,] heights = new float[data.heightmapResolution, data.heightmapResolution]; data.SetHeights(0, 0, heights); // Очищаем деревья data.treeInstances = new TreeInstance[0]; data.treePrototypes = new TreePrototype[0]; // Полная очистка травы data.detailPrototypes = new DetailPrototype[0]; data.RefreshPrototypes(); // Очищаем текстуры if (data.terrainLayers.Length > 0) { float[,,] alphaMap = new float[data.alphamapWidth, data.alphamapHeight, data.terrainLayers.Length]; data.SetAlphamaps(0, 0, alphaMap); } Debug.Log("Террейн полностью очищен!"); } } } } #endif