修改了保存预制体的bug
This commit is contained in:
parent
5419e9b08f
commit
a826fdaafd
@ -197,68 +197,142 @@ namespace MindPowerSdk.Editor
|
||||
Debug.Log($"map size:({map.Width},{map.Height}) | section size:({map.SectionWidth},{map.SectionHeight})");
|
||||
|
||||
// 调用生成 Terrain 的方法,传入 mapName 作为标识
|
||||
TerrainGenerator.GenTerrain(map, chunkSize, terrainMaterial, parent.transform, entry.mapName,
|
||||
var textureList = TerrainGenerator.GenTerrain(map, chunkSize, terrainMaterial, parent.transform,
|
||||
entry.mapName,
|
||||
entry.isSmoothing);
|
||||
Debug.Log($"生成地图:{entry.mapName}\n数据路径:{mapDataPath}\n资源路径:{resourceDataPath}");
|
||||
|
||||
CreateMapObject(resourceDataPath, map, parent.transform);
|
||||
// 创建场景对象并关联到地形
|
||||
CreateMapObjectsWithTerrain(resourceDataPath, map, parent.transform, entry.mapName, chunkSize);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CreateMapObject(string objPath, MPMap map, Transform parent)
|
||||
/// <summary>
|
||||
/// 创建场景对象并智能地关联到地形系统
|
||||
/// </summary>
|
||||
/// <param name="objPath">场景对象文件路径</param>
|
||||
/// <param name="map">地图数据</param>
|
||||
/// <param name="parent">父物体</param>
|
||||
/// <param name="mapName">地图名称</param>
|
||||
/// <param name="chunkSize">区块大小</param>
|
||||
void CreateMapObjectsWithTerrain(string objPath, MPMap map, Transform parent, string mapName, int chunkSize)
|
||||
{
|
||||
// 加载场景对象数据
|
||||
CSceneObjSet sceneObjSet = new CSceneObjSet();
|
||||
sceneObjSet.LoadBin("Assets/Resources/sceneobjinfo.bin");
|
||||
|
||||
SceneObjFile sceneObjFile = new SceneObjFile();
|
||||
sceneObjFile.Load(objPath);
|
||||
|
||||
// 找到地形根节点
|
||||
Transform terrainRoot = parent.Find(mapName);
|
||||
if (terrainRoot == null)
|
||||
{
|
||||
Debug.LogError($"找不到地形根节点: {mapName}");
|
||||
return;
|
||||
}
|
||||
|
||||
// 创建场景对象根节点
|
||||
string objName = Path.GetFileNameWithoutExtension(objPath);
|
||||
GameObject root = new GameObject($"{objName}_SceneAssetsRoot");
|
||||
root.transform.SetParent(parent);
|
||||
GameObject sceneRoot = new GameObject($"{objName}_SceneObjects");
|
||||
sceneRoot.transform.SetParent(parent);
|
||||
// 收集所有地形组件,用于场景对象关联
|
||||
Dictionary<Vector2Int, Terrain> terrainMap = new Dictionary<Vector2Int, Terrain>();
|
||||
CollectTerrainComponents(terrainRoot, terrainMap, chunkSize);
|
||||
|
||||
// 遍历所有场景对象
|
||||
for (int y = 0; y < sceneObjFile.FileHeader.SectionCntY; y++)
|
||||
{
|
||||
for (int x = 0; x < sceneObjFile.FileHeader.SectionCntX; x++)
|
||||
{
|
||||
var list = sceneObjFile.objInfos[x, y];
|
||||
if (list is null) continue;
|
||||
var objList = sceneObjFile.objInfos[x, y];
|
||||
if (objList == null) continue;
|
||||
|
||||
foreach (var sceneObjInfo in list)
|
||||
foreach (var sceneObjInfo in objList)
|
||||
{
|
||||
if (sceneObjInfo.GetTypeId() == 1)
|
||||
continue;
|
||||
if (sceneObjInfo.GetTypeId() == 1) continue; // 跳过特效物件
|
||||
|
||||
int id = sceneObjInfo.GetID();
|
||||
|
||||
CSceneObjInfo modeInfo = sceneObjSet.Get(id);
|
||||
|
||||
Vector3 pos = new Vector3(sceneObjInfo.X / 100f, sceneObjInfo.HeightOff / 100f,
|
||||
sceneObjInfo.Y / 100f * -1f);
|
||||
float mapHeight = map.GetHeight(pos.x, (sceneObjInfo.Y / 100f));
|
||||
pos.y += mapHeight;
|
||||
// 计算世界坐标,确保与地形坐标系一致
|
||||
// 地图坐标系转换:X不变,Z轴翻转以匹配地形位置计算
|
||||
float objX = sceneObjInfo.X / 100f;
|
||||
float objY = sceneObjInfo.HeightOff / 100f;
|
||||
float objZ = map.Height - (sceneObjInfo.Y / 100f); // 与地形位置计算保持一致
|
||||
|
||||
//Quaternion rot = Quaternion.AngleAxis(sceneObjInfo.YawAngle, Vector3.up);
|
||||
string modePath = "Assets/Resources/Model/Scene/" +
|
||||
Vector3 worldPos = new Vector3(objX, objY, objZ);
|
||||
|
||||
// 获取地形高度
|
||||
float mapHeight = map.GetHeight(worldPos.x, sceneObjInfo.Y / 100f);
|
||||
worldPos.y += mapHeight;
|
||||
|
||||
// 加载模型资源
|
||||
string modelPath = "Assets/Resources/Model/Scene/" +
|
||||
Path.GetFileNameWithoutExtension(modeInfo.szDataName) + ".lmo.obj";
|
||||
Object mode = AssetDatabase.LoadAssetAtPath<Object>(modePath);
|
||||
if (mode)
|
||||
GameObject prefab = AssetDatabase.LoadAssetAtPath<GameObject>(modelPath);
|
||||
|
||||
if (prefab == null)
|
||||
{
|
||||
GameObject goModel = (GameObject)GameObject.Instantiate(mode);
|
||||
goModel.transform.position = pos;
|
||||
goModel.transform.localScale = new Vector3(1, 1, -1);
|
||||
Vector3 e = goModel.transform.eulerAngles;
|
||||
goModel.transform.rotation = Quaternion.Euler(e.x, sceneObjInfo.YawAngle, e.z);
|
||||
goModel.name = $"[{id}]{mode.name}";
|
||||
goModel.transform.SetParent(root.transform);
|
||||
// Debug.Log(goModel);
|
||||
Debug.LogError($"模型未找到: {modelPath}");
|
||||
continue;
|
||||
}
|
||||
else
|
||||
|
||||
// 简化处理:统一作为场景对象处理,确保正确的地形贴合
|
||||
GameObject instance = UnityEngine.Object.Instantiate(prefab);
|
||||
instance.transform.position = worldPos;
|
||||
instance.transform.localScale = new Vector3(1, 1, -1);
|
||||
Vector3 euler = instance.transform.eulerAngles;
|
||||
instance.transform.rotation = Quaternion.Euler(euler.x, sceneObjInfo.YawAngle, euler.z);
|
||||
instance.name = $"[{id}]{prefab.name}";
|
||||
instance.transform.SetParent(sceneRoot.transform);
|
||||
|
||||
// 添加地形关联信息(可选)
|
||||
// 计算该对象属于哪个地形块
|
||||
int terrainX = Mathf.FloorToInt(worldPos.x / chunkSize);
|
||||
// 因为我们的地形Z位置是 map.Height - startY - resY,所以需要反向计算
|
||||
int originalMapY = (int)(map.Height - worldPos.z);
|
||||
int terrainY = Mathf.FloorToInt(originalMapY / chunkSize);
|
||||
Vector2Int terrainCoord = new Vector2Int(terrainX, terrainY);
|
||||
|
||||
if (terrainMap.TryGetValue(terrainCoord, out Terrain associatedTerrain))
|
||||
{
|
||||
Debug.LogError($"mode not found: {modePath}");
|
||||
// 可以在这里添加对象与地形的关联逻辑
|
||||
// 例如:给对象添加一个组件来记录关联的地形
|
||||
Debug.Log($"对象 {instance.name} 关联到地形: {associatedTerrain.name}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Debug.Log($"场景对象处理完成 - 总对象数: {sceneRoot.transform.childCount}");
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 收集所有地形组件
|
||||
/// </summary>
|
||||
void CollectTerrainComponents(Transform terrainRoot, Dictionary<Vector2Int, Terrain> terrainMap, int chunkSize)
|
||||
{
|
||||
foreach (Transform child in terrainRoot)
|
||||
{
|
||||
Terrain terrain = child.GetComponent<Terrain>();
|
||||
if (terrain != null)
|
||||
{
|
||||
// 从地形名称解析坐标 (terrain_i_j)
|
||||
string[] parts = child.name.Split('_');
|
||||
if (parts.Length >= 3 &&
|
||||
int.TryParse(parts[1], out int i) &&
|
||||
int.TryParse(parts[2], out int j))
|
||||
{
|
||||
// 存储地形块的索引坐标和对应的Terrain组件
|
||||
Vector2Int coord = new Vector2Int(j, i); // j是x索引, i是y索引
|
||||
terrainMap[coord] = terrain;
|
||||
|
||||
Debug.Log($"收集地形块: {child.name} -> 坐标({j}, {i}) -> 世界位置{terrain.transform.position}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -295,11 +369,12 @@ namespace MindPowerSdk.Editor
|
||||
// 遍历每个地图生成的子对象
|
||||
foreach (Transform mapTransform in parent.transform)
|
||||
{
|
||||
if (mapTransform.name.Contains("SceneAssetsRoot"))
|
||||
if (mapTransform.name.Contains("SceneObjects") || mapTransform.name.Contains("SceneAssetsRoot"))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
string folder = EditorUtility.OpenFolderPanel("选择预制体路径", "", "") + $"/{mapTransform.name}/";
|
||||
string prefabPath = folder + mapTransform.name + ".prefab";
|
||||
|
||||
@ -311,6 +386,7 @@ namespace MindPowerSdk.Editor
|
||||
}
|
||||
|
||||
string materialDir = Path.GetDirectoryName(prefabPath) + "/Material";
|
||||
|
||||
if (!Directory.Exists(materialDir))
|
||||
{
|
||||
Directory.CreateDirectory(materialDir);
|
||||
@ -328,24 +404,28 @@ namespace MindPowerSdk.Editor
|
||||
var terrain = child.GetComponent<Terrain>();
|
||||
var terrainCollider = child.GetComponent<TerrainCollider>();
|
||||
|
||||
|
||||
Debug.Log(child.name);
|
||||
if (terrain != null)
|
||||
{
|
||||
materialDir = materialDir.Replace(@"\", "/");
|
||||
materialDir = materialDir.Replace(Application.dataPath, "Assets");
|
||||
materialDir = $"{materialDir}/{mapTransform.name}.mat";
|
||||
var materialFiled = $"{materialDir}/{mapTransform.name}_{child.name}.mat";
|
||||
var terrainDataPath = directory.Replace(@"\", "/");
|
||||
terrainDataPath = terrainDataPath.Replace(Application.dataPath, "Assets");
|
||||
terrainDataPath = $"{terrainDataPath}/{mapTransform.name}.asset";
|
||||
if (File.Exists(materialDir))
|
||||
AssetDatabase.DeleteAsset(materialDir);
|
||||
terrainDataPath = $"{terrainDataPath}/{mapTransform.name}_{child.name}.asset";
|
||||
if (File.Exists(materialFiled))
|
||||
AssetDatabase.DeleteAsset(materialFiled);
|
||||
if (File.Exists(terrainDataPath))
|
||||
AssetDatabase.DeleteAsset(terrainDataPath);
|
||||
AssetDatabase.SaveAssets();
|
||||
AssetDatabase.Refresh();
|
||||
AssetDatabase.CreateAsset(terrain.materialTemplate, materialDir);
|
||||
// 保存材质和地形数据
|
||||
AssetDatabase.CreateAsset(terrain.materialTemplate, materialFiled);
|
||||
AssetDatabase.CreateAsset(terrainCollider.terrainData, terrainDataPath);
|
||||
|
||||
// 确保地形数据的引用正确
|
||||
terrain.terrainData = terrainCollider.terrainData;
|
||||
|
||||
// 把 terrain.materialTemplate 中所有的图片保存下来
|
||||
foreach (var texturePropertyName in terrain.materialTemplate.GetTexturePropertyNames())
|
||||
{
|
||||
@ -355,7 +435,7 @@ namespace MindPowerSdk.Editor
|
||||
Debug.Log($"保存材质贴图:{texturePropertyName} | {texture.name}");
|
||||
string texturePath = texturesDir.Replace(@"\", "/");
|
||||
texturePath = texturePath.Replace(Application.dataPath, "Assets");
|
||||
var textName = texturePropertyName;
|
||||
var textName = $"{texturePropertyName}_{child.name}";
|
||||
texturePath = $"{texturePath}/{textName}.png";
|
||||
|
||||
// 检查纹理是否已经是项目中的资产
|
||||
@ -377,7 +457,8 @@ namespace MindPowerSdk.Editor
|
||||
byte[] bytes = texture2D.EncodeToPNG();
|
||||
if (bytes != null)
|
||||
{
|
||||
File.WriteAllBytes(texturePath.Replace("Assets", Application.dataPath), bytes);
|
||||
File.WriteAllBytes(texturePath.Replace("Assets", Application.dataPath),
|
||||
bytes);
|
||||
AssetDatabase.ImportAsset(texturePath);
|
||||
Debug.Log($"导出材质贴图:{texturePath}");
|
||||
}
|
||||
@ -402,7 +483,7 @@ namespace MindPowerSdk.Editor
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据传入的类型名称(如“草”或“树”)进行模糊匹配,
|
||||
/// 根据传入的类型名称(如"草"或"树")进行模糊匹配,
|
||||
/// 遍历材质映射配置,返回第一个匹配的目标材质,未匹配返回 null。
|
||||
/// </summary>
|
||||
public Material GetMappedMaterial(string typeName)
|
||||
|
@ -13,8 +13,8 @@ public static class TerrainGenerator
|
||||
/// <param name="chunkSize">每块区域的瓦片数(例如 64)</param>
|
||||
/// <param name="terrainMaterial">基于自定义Shader(例如 Custom/TerrainUVBlendShader)的材质</param>
|
||||
/// <param name="parent">生成的 Terrain 父节点</param>
|
||||
/// <param name="isSmoothing"></param>
|
||||
/// <param name="entryMapName"></param>
|
||||
/// <param name="isSmoothing"></param>
|
||||
public static List<Texture> GenTerrain(MPMap map, int chunkSize, Material terrainMaterial, Transform parent,
|
||||
string entryMapName, bool isSmoothing = true)
|
||||
{
|
||||
@ -26,95 +26,199 @@ public static class TerrainGenerator
|
||||
int yCnt = Mathf.CeilToInt((float)map.Height / chunkSize);
|
||||
var terrainMap = new GameObject(entryMapName);
|
||||
terrainMap.transform.parent = parent;
|
||||
|
||||
Debug.Log($"开始生成地形: 地图尺寸({map.Width}, {map.Height}), 块大小={chunkSize}, 将生成{xCnt}x{yCnt}={xCnt*yCnt}个地形块");
|
||||
|
||||
for (int i = 0; i < yCnt; i++)
|
||||
{
|
||||
for (int j = 0; j < xCnt; j++)
|
||||
{
|
||||
int startX = j * chunkSize;
|
||||
int startY = i * chunkSize;
|
||||
// 为保证边界内不越界,右侧和上侧减1
|
||||
int endX = Mathf.Min(startX + chunkSize, map.Width - 1);
|
||||
int endY = Mathf.Min(startY + chunkSize, map.Height - 1);
|
||||
int resX = (endX - startX) + 1; // 横向瓦片数
|
||||
int resY = (endY - startY) + 1; // 纵向瓦片数
|
||||
// 修复:正确计算边界,确保最后一块包含所有剩余像素
|
||||
int endX = Mathf.Min(startX + chunkSize, map.Width);
|
||||
int endY = Mathf.Min(startY + chunkSize, map.Height);
|
||||
int resX = endX - startX; // 横向瓦片数
|
||||
int resY = endY - startY; // 纵向瓦片数
|
||||
|
||||
// 为了保证生成的 Terrain 与原始地图高度一致,并且位置正确,需要使用固定分辨率
|
||||
int newRes = 4097;
|
||||
Debug.Log($"地形块[{i},{j}]: startX={startX}, startY={startY}, endX={endX}, endY={endY}, resX={resX}, resY={resY}");
|
||||
|
||||
// 优化:使用合理的分辨率,避免过高的分辨率导致性能和兼容性问题
|
||||
// 根据chunk大小动态计算分辨率,确保每个tile至少有4个像素
|
||||
int newRes = Mathf.NextPowerOfTwo(Mathf.Max(resX, resY) * 4) + 1;
|
||||
newRes = Mathf.Clamp(newRes, 129, 1025); // 限制在合理范围内
|
||||
|
||||
// 创建 TerrainData,设置高度图和混合图分辨率以及尺寸
|
||||
TerrainData terrainData = new TerrainData();
|
||||
terrainData.heightmapResolution = newRes;
|
||||
terrainData.alphamapResolution = newRes;
|
||||
terrainData.size = new Vector3((resX - 1), globalMax - globalMin, (resY - 1));
|
||||
|
||||
// 设置地形尺寸,确保与实际数据对应
|
||||
float terrainWidth = resX;
|
||||
float terrainLength = resY;
|
||||
float terrainHeight = globalMax - globalMin;
|
||||
terrainData.size = new Vector3(terrainWidth, terrainHeight, terrainLength);
|
||||
|
||||
// 设置地形的边界处理模式,确保能被正确拆分
|
||||
#if UNITY_EDITOR
|
||||
// 这些设置有助于地形被第三方工具正确处理
|
||||
terrainData.wavingGrassStrength = 0.5f;
|
||||
terrainData.wavingGrassAmount = 0.5f;
|
||||
terrainData.wavingGrassSpeed = 0.5f;
|
||||
terrainData.wavingGrassTint = Color.white;
|
||||
#endif
|
||||
|
||||
// 生成高度图(归一化到 [0,1])
|
||||
float[,] heights = GenerateHeightMap(map, startX, startY, resX, resY, newRes, globalMin, globalMax,
|
||||
isSmoothing);
|
||||
terrainData.SetHeights(0, 0, heights);
|
||||
|
||||
// ---------------------- 新增部分 -------------------------
|
||||
// 生成贴图编号和遮罩贴图(调用你已有的 GenTxtNoTexture 方法)
|
||||
Debug.LogWarning($"{startX} {startY} {chunkSize}");
|
||||
(Texture2D texNo, Texture2D maskNo) = map.GenTxtNoTexture((short)startX, (short)startY, chunkSize);
|
||||
// ---------------------- 贴图设置部分 -------------------------
|
||||
// 生成贴图编号和遮罩贴图
|
||||
// 注意:贴图采样使用原始的resX, resY (不包含+1边界)
|
||||
int texResX = endX - startX;
|
||||
int texResY = endY - startY;
|
||||
Debug.LogWarning($"生成地形块: startX={startX}, startY={startY}, texResX={texResX}, texResY={texResY}");
|
||||
(Texture2D texNo, Texture2D maskNo) = map.GenTxtNoTexture((short)startX, (short)startY, texResX);
|
||||
|
||||
textureList.Add(texNo);
|
||||
textureList.Add(maskNo);
|
||||
// 设置每个 tile 在辅助 baked UV 贴图中希望的像素尺寸(建议不小于 4)
|
||||
int cellPixelSize = 7;
|
||||
|
||||
|
||||
if (terrainMaterial== null)
|
||||
{
|
||||
Debug.LogError("地形材质未设置,请提供一个自定义材质或使用内置材质系统。");
|
||||
return null;
|
||||
}
|
||||
// 创建材质实例,并设置贴图
|
||||
Material matInstance = new Material(terrainMaterial);
|
||||
matInstance.SetTexture("_TexNo", texNo);
|
||||
matInstance.SetTexture("_MaskNo", maskNo);
|
||||
|
||||
|
||||
// 计算每个 tile 在 UV 空间内的尺寸(通常为 1/chunkSize)
|
||||
Vector2 mainTileScale = new Vector2(1.0f / chunkSize, 1.0f / chunkSize);
|
||||
// 计算每个 tile 在 UV 空间内的尺寸
|
||||
Vector2 mainTileScale = new Vector2(1.0f / texResX, 1.0f / texResY);
|
||||
matInstance.SetVector("_MainTileOffset", new Vector4(mainTileScale.x, mainTileScale.y, 0, 0));
|
||||
|
||||
// 设置 Terrain 尺寸参数,Shader 内部可根据该参数计算全局 UV0(X:宽度,Z:高度)
|
||||
matInstance.SetVector("_TerrainSize", new Vector4((resX - 1), (resY - 1), 0, 0));
|
||||
// 设置 Terrain 尺寸参数
|
||||
matInstance.SetVector("_TerrainSize", new Vector4(resX, resY, 0, 0));
|
||||
matInstance.SetFloat("_Repeat", resX);
|
||||
// ---------------------- 贴图设置结束 -------------------------
|
||||
|
||||
matInstance.SetFloat("_Repeat", (resX - 1));
|
||||
// ---------------------- 新增部分结束 -------------------------
|
||||
|
||||
// 生成 Terrain 游戏对象并设置位置(Y 坐标偏移 globalMin)
|
||||
// 生成 Terrain 游戏对象并设置位置
|
||||
GameObject terrainGO = Terrain.CreateTerrainGameObject(terrainData);
|
||||
terrainGO.name = $"terrain_{i}_{j}";
|
||||
terrainGO.transform.parent = terrainMap.transform;
|
||||
terrainGO.transform.position = new Vector3(startX, globalMin, startY - chunkSize + 1);
|
||||
Debug.Log($"terrainGO: {terrainGO.name} | pos: {terrainGO.transform.position}");
|
||||
// 设置 Terrain 为自定义材质模式,并赋予生成的材质实例
|
||||
|
||||
// 修复:正确的位置计算,确保地形块能正确拼接
|
||||
// 地图坐标系转换到Unity坐标系:
|
||||
// X轴保持不变,Y轴是高度,Z轴需要翻转
|
||||
// 原地图的Y=0对应Unity的Z=map.Height,Y=map.Height对应Unity的Z=0
|
||||
float unityPosX = startX;
|
||||
float unityPosY = globalMin;
|
||||
float unityPosZ = map.Height - startY - resY;
|
||||
|
||||
Vector3 terrainPosition = new Vector3(unityPosX, unityPosY, unityPosZ);
|
||||
terrainGO.transform.position = terrainPosition;
|
||||
|
||||
Debug.Log($"地形块 {terrainGO.name}: 位置={terrainPosition}, 尺寸={terrainData.size}, 高度图分辨率={newRes}");
|
||||
|
||||
// 设置 Terrain 组件
|
||||
Terrain terrainComponent = terrainGO.GetComponent<Terrain>();
|
||||
TerrainCollider terrainCollider = terrainGO.GetComponent<TerrainCollider>();
|
||||
|
||||
// 设置地形渲染和性能参数
|
||||
terrainComponent.heightmapPixelError = 5;
|
||||
terrainComponent.basemapDistance = 1000;
|
||||
terrainComponent.shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.On;
|
||||
terrainComponent.reflectionProbeUsage = UnityEngine.Rendering.ReflectionProbeUsage.BlendProbes;
|
||||
|
||||
// 确保TerrainCollider正确关联TerrainData
|
||||
if (terrainCollider != null)
|
||||
{
|
||||
terrainCollider.terrainData = terrainData;
|
||||
}
|
||||
|
||||
// 优化:为了更好的兼容性,同时支持自定义材质和内置材质系统
|
||||
if (terrainMaterial != null)
|
||||
{
|
||||
terrainComponent.materialType = Terrain.MaterialType.Custom;
|
||||
terrainComponent.materialTemplate = matInstance;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 如果没有自定义材质,使用内置材质系统
|
||||
terrainComponent.materialType = Terrain.MaterialType.BuiltInStandard;
|
||||
}
|
||||
|
||||
// 设置地形的相邻关系,确保无缝拼接
|
||||
SetupTerrainNeighbors(terrainMap.transform, i, j, yCnt, xCnt);
|
||||
}
|
||||
}
|
||||
|
||||
return textureList;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置地形的相邻关系,确保地形块之间无缝拼接
|
||||
/// </summary>
|
||||
private static void SetupTerrainNeighbors(Transform terrainParent, int i, int j, int yCnt, int xCnt)
|
||||
{
|
||||
// 获取当前地形
|
||||
Transform currentTerrain = terrainParent.Find($"terrain_{i}_{j}");
|
||||
if (currentTerrain == null) return;
|
||||
|
||||
// 以下方法与原有代码一致
|
||||
Terrain current = currentTerrain.GetComponent<Terrain>();
|
||||
if (current == null) return;
|
||||
|
||||
// 设置相邻地形
|
||||
Terrain left = GetTerrainAt(terrainParent, i, j - 1, yCnt, xCnt);
|
||||
Terrain right = GetTerrainAt(terrainParent, i, j + 1, yCnt, xCnt);
|
||||
Terrain top = GetTerrainAt(terrainParent, i + 1, j, yCnt, xCnt);
|
||||
Terrain bottom = GetTerrainAt(terrainParent, i - 1, j, yCnt, xCnt);
|
||||
|
||||
current.SetNeighbors(left, top, right, bottom);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取指定位置的地形组件
|
||||
/// </summary>
|
||||
private static Terrain GetTerrainAt(Transform terrainParent, int i, int j, int yCnt, int xCnt)
|
||||
{
|
||||
if (i < 0 || i >= yCnt || j < 0 || j >= xCnt) return null;
|
||||
|
||||
Transform terrainTransform = terrainParent.Find($"terrain_{i}_{j}");
|
||||
return terrainTransform?.GetComponent<Terrain>();
|
||||
}
|
||||
|
||||
private static float[,] GenerateHeightMap(MPMap map, int startX, int startY, int resX, int resY, int newRes,
|
||||
float globalMin, float globalMax, bool isSmoothing)
|
||||
{
|
||||
// 采集原始高度数据,并归一化到 [0,1]
|
||||
float[,] fullHeights = new float[resY, resX];
|
||||
for (int z = 0; z < resY; z++)
|
||||
// 注意:为了确保地形连续性,我们需要包含边界像素
|
||||
int sampleResX = resX + 1;
|
||||
int sampleResY = resY + 1;
|
||||
float[,] fullHeights = new float[sampleResY, sampleResX];
|
||||
|
||||
for (int z = 0; z < sampleResY; z++)
|
||||
{
|
||||
for (int x = 0; x < resX; x++)
|
||||
for (int x = 0; x < sampleResX; x++)
|
||||
{
|
||||
int worldX = startX + x;
|
||||
int worldY = startY + z;
|
||||
|
||||
// 边界检查,确保不超出地图范围
|
||||
worldX = Mathf.Clamp(worldX, 0, map.Width - 1);
|
||||
worldY = Mathf.Clamp(worldY, 0, map.Height - 1);
|
||||
|
||||
MPTile tile = map.GetTile(worldX, worldY);
|
||||
float norm = (globalMax - globalMin) > 0 ? (tile.Height - globalMin) / (globalMax - globalMin) : 0f;
|
||||
fullHeights[resY - z - 1, x] = norm;
|
||||
// 修复:确保高度图方向与贴图采样一致,Y轴需要翻转以匹配Unity的Terrain坐标系
|
||||
fullHeights[sampleResY - z - 1, x] = norm;
|
||||
}
|
||||
}
|
||||
|
||||
// 更新采样参数
|
||||
resX = sampleResX;
|
||||
resY = sampleResY;
|
||||
|
||||
// 利用双三次插值生成新分辨率高度图
|
||||
float[,] resampled = new float[newRes, newRes];
|
||||
float scaleX = (float)(resX - 1) / (newRes - 1);
|
||||
|
Loading…
x
Reference in New Issue
Block a user