修改了保存预制体的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})");
|
Debug.Log($"map size:({map.Width},{map.Height}) | section size:({map.SectionWidth},{map.SectionHeight})");
|
||||||
|
|
||||||
// 调用生成 Terrain 的方法,传入 mapName 作为标识
|
// 调用生成 Terrain 的方法,传入 mapName 作为标识
|
||||||
TerrainGenerator.GenTerrain(map, chunkSize, terrainMaterial, parent.transform, entry.mapName,
|
var textureList = TerrainGenerator.GenTerrain(map, chunkSize, terrainMaterial, parent.transform,
|
||||||
entry.isSmoothing);
|
entry.mapName,
|
||||||
|
entry.isSmoothing);
|
||||||
Debug.Log($"生成地图:{entry.mapName}\n数据路径:{mapDataPath}\n资源路径:{resourceDataPath}");
|
Debug.Log($"生成地图:{entry.mapName}\n数据路径:{mapDataPath}\n资源路径:{resourceDataPath}");
|
||||||
|
|
||||||
CreateMapObject(resourceDataPath, map, parent.transform);
|
// 创建场景对象并关联到地形
|
||||||
|
CreateMapObjectsWithTerrain(resourceDataPath, map, parent.transform, entry.mapName, chunkSize);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
void CreateMapObject(string objPath, MPMap map, Transform parent)
|
/// 创建场景对象并智能地关联到地形系统
|
||||||
|
/// </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();
|
CSceneObjSet sceneObjSet = new CSceneObjSet();
|
||||||
sceneObjSet.LoadBin("Assets/Resources/sceneobjinfo.bin");
|
sceneObjSet.LoadBin("Assets/Resources/sceneobjinfo.bin");
|
||||||
|
|
||||||
SceneObjFile sceneObjFile = new SceneObjFile();
|
SceneObjFile sceneObjFile = new SceneObjFile();
|
||||||
sceneObjFile.Load(objPath);
|
sceneObjFile.Load(objPath);
|
||||||
string objName = Path.GetFileNameWithoutExtension(objPath);
|
|
||||||
GameObject root = new GameObject($"{objName}_SceneAssetsRoot");
|
// 找到地形根节点
|
||||||
root.transform.SetParent(parent);
|
Transform terrainRoot = parent.Find(mapName);
|
||||||
|
if (terrainRoot == null)
|
||||||
|
{
|
||||||
|
Debug.LogError($"找不到地形根节点: {mapName}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建场景对象根节点
|
||||||
|
string objName = Path.GetFileNameWithoutExtension(objPath);
|
||||||
|
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 y = 0; y < sceneObjFile.FileHeader.SectionCntY; y++)
|
||||||
{
|
{
|
||||||
for (int x = 0; x < sceneObjFile.FileHeader.SectionCntX; x++)
|
for (int x = 0; x < sceneObjFile.FileHeader.SectionCntX; x++)
|
||||||
{
|
{
|
||||||
var list = sceneObjFile.objInfos[x, y];
|
var objList = sceneObjFile.objInfos[x, y];
|
||||||
if (list is null) continue;
|
if (objList == null) continue;
|
||||||
|
|
||||||
foreach (var sceneObjInfo in list)
|
foreach (var sceneObjInfo in objList)
|
||||||
{
|
{
|
||||||
if (sceneObjInfo.GetTypeId() == 1)
|
if (sceneObjInfo.GetTypeId() == 1) continue; // 跳过特效物件
|
||||||
continue;
|
|
||||||
|
|
||||||
int id = sceneObjInfo.GetID();
|
|
||||||
|
|
||||||
|
int id = sceneObjInfo.GetID();
|
||||||
CSceneObjInfo modeInfo = sceneObjSet.Get(id);
|
CSceneObjInfo modeInfo = sceneObjSet.Get(id);
|
||||||
|
|
||||||
Vector3 pos = new Vector3(sceneObjInfo.X / 100f, sceneObjInfo.HeightOff / 100f,
|
// 计算世界坐标,确保与地形坐标系一致
|
||||||
sceneObjInfo.Y / 100f * -1f);
|
// 地图坐标系转换:X不变,Z轴翻转以匹配地形位置计算
|
||||||
float mapHeight = map.GetHeight(pos.x, (sceneObjInfo.Y / 100f));
|
float objX = sceneObjInfo.X / 100f;
|
||||||
pos.y += mapHeight;
|
float objY = sceneObjInfo.HeightOff / 100f;
|
||||||
|
float objZ = map.Height - (sceneObjInfo.Y / 100f); // 与地形位置计算保持一致
|
||||||
|
|
||||||
//Quaternion rot = Quaternion.AngleAxis(sceneObjInfo.YawAngle, Vector3.up);
|
Vector3 worldPos = new Vector3(objX, objY, objZ);
|
||||||
string modePath = "Assets/Resources/Model/Scene/" +
|
|
||||||
Path.GetFileNameWithoutExtension(modeInfo.szDataName) + ".lmo.obj";
|
// 获取地形高度
|
||||||
Object mode = AssetDatabase.LoadAssetAtPath<Object>(modePath);
|
float mapHeight = map.GetHeight(worldPos.x, sceneObjInfo.Y / 100f);
|
||||||
if (mode)
|
worldPos.y += mapHeight;
|
||||||
|
|
||||||
|
// 加载模型资源
|
||||||
|
string modelPath = "Assets/Resources/Model/Scene/" +
|
||||||
|
Path.GetFileNameWithoutExtension(modeInfo.szDataName) + ".lmo.obj";
|
||||||
|
GameObject prefab = AssetDatabase.LoadAssetAtPath<GameObject>(modelPath);
|
||||||
|
|
||||||
|
if (prefab == null)
|
||||||
{
|
{
|
||||||
GameObject goModel = (GameObject)GameObject.Instantiate(mode);
|
Debug.LogError($"模型未找到: {modelPath}");
|
||||||
goModel.transform.position = pos;
|
continue;
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
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)
|
foreach (Transform mapTransform in parent.transform)
|
||||||
{
|
{
|
||||||
if (mapTransform.name.Contains("SceneAssetsRoot"))
|
if (mapTransform.name.Contains("SceneObjects") || mapTransform.name.Contains("SceneAssetsRoot"))
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
string folder = EditorUtility.OpenFolderPanel("选择预制体路径", "", "") + $"/{mapTransform.name}/";
|
string folder = EditorUtility.OpenFolderPanel("选择预制体路径", "", "") + $"/{mapTransform.name}/";
|
||||||
string prefabPath = folder + mapTransform.name + ".prefab";
|
string prefabPath = folder + mapTransform.name + ".prefab";
|
||||||
|
|
||||||
@ -311,6 +386,7 @@ namespace MindPowerSdk.Editor
|
|||||||
}
|
}
|
||||||
|
|
||||||
string materialDir = Path.GetDirectoryName(prefabPath) + "/Material";
|
string materialDir = Path.GetDirectoryName(prefabPath) + "/Material";
|
||||||
|
|
||||||
if (!Directory.Exists(materialDir))
|
if (!Directory.Exists(materialDir))
|
||||||
{
|
{
|
||||||
Directory.CreateDirectory(materialDir);
|
Directory.CreateDirectory(materialDir);
|
||||||
@ -328,24 +404,28 @@ namespace MindPowerSdk.Editor
|
|||||||
var terrain = child.GetComponent<Terrain>();
|
var terrain = child.GetComponent<Terrain>();
|
||||||
var terrainCollider = child.GetComponent<TerrainCollider>();
|
var terrainCollider = child.GetComponent<TerrainCollider>();
|
||||||
|
|
||||||
|
Debug.Log(child.name);
|
||||||
if (terrain != null)
|
if (terrain != null)
|
||||||
{
|
{
|
||||||
materialDir = materialDir.Replace(@"\", "/");
|
materialDir = materialDir.Replace(@"\", "/");
|
||||||
materialDir = materialDir.Replace(Application.dataPath, "Assets");
|
materialDir = materialDir.Replace(Application.dataPath, "Assets");
|
||||||
materialDir = $"{materialDir}/{mapTransform.name}.mat";
|
var materialFiled = $"{materialDir}/{mapTransform.name}_{child.name}.mat";
|
||||||
var terrainDataPath = directory.Replace(@"\", "/");
|
var terrainDataPath = directory.Replace(@"\", "/");
|
||||||
terrainDataPath = terrainDataPath.Replace(Application.dataPath, "Assets");
|
terrainDataPath = terrainDataPath.Replace(Application.dataPath, "Assets");
|
||||||
terrainDataPath = $"{terrainDataPath}/{mapTransform.name}.asset";
|
terrainDataPath = $"{terrainDataPath}/{mapTransform.name}_{child.name}.asset";
|
||||||
if (File.Exists(materialDir))
|
if (File.Exists(materialFiled))
|
||||||
AssetDatabase.DeleteAsset(materialDir);
|
AssetDatabase.DeleteAsset(materialFiled);
|
||||||
if (File.Exists(terrainDataPath))
|
if (File.Exists(terrainDataPath))
|
||||||
AssetDatabase.DeleteAsset(terrainDataPath);
|
AssetDatabase.DeleteAsset(terrainDataPath);
|
||||||
AssetDatabase.SaveAssets();
|
AssetDatabase.SaveAssets();
|
||||||
AssetDatabase.Refresh();
|
AssetDatabase.Refresh();
|
||||||
AssetDatabase.CreateAsset(terrain.materialTemplate, materialDir);
|
// 保存材质和地形数据
|
||||||
|
AssetDatabase.CreateAsset(terrain.materialTemplate, materialFiled);
|
||||||
AssetDatabase.CreateAsset(terrainCollider.terrainData, terrainDataPath);
|
AssetDatabase.CreateAsset(terrainCollider.terrainData, terrainDataPath);
|
||||||
|
|
||||||
|
// 确保地形数据的引用正确
|
||||||
|
terrain.terrainData = terrainCollider.terrainData;
|
||||||
|
|
||||||
// 把 terrain.materialTemplate 中所有的图片保存下来
|
// 把 terrain.materialTemplate 中所有的图片保存下来
|
||||||
foreach (var texturePropertyName in terrain.materialTemplate.GetTexturePropertyNames())
|
foreach (var texturePropertyName in terrain.materialTemplate.GetTexturePropertyNames())
|
||||||
{
|
{
|
||||||
@ -355,9 +435,9 @@ namespace MindPowerSdk.Editor
|
|||||||
Debug.Log($"保存材质贴图:{texturePropertyName} | {texture.name}");
|
Debug.Log($"保存材质贴图:{texturePropertyName} | {texture.name}");
|
||||||
string texturePath = texturesDir.Replace(@"\", "/");
|
string texturePath = texturesDir.Replace(@"\", "/");
|
||||||
texturePath = texturePath.Replace(Application.dataPath, "Assets");
|
texturePath = texturePath.Replace(Application.dataPath, "Assets");
|
||||||
var textName = texturePropertyName;
|
var textName = $"{texturePropertyName}_{child.name}";
|
||||||
texturePath = $"{texturePath}/{textName}.png";
|
texturePath = $"{texturePath}/{textName}.png";
|
||||||
|
|
||||||
// 检查纹理是否已经是项目中的资产
|
// 检查纹理是否已经是项目中的资产
|
||||||
string originalPath = AssetDatabase.GetAssetPath(texture);
|
string originalPath = AssetDatabase.GetAssetPath(texture);
|
||||||
if (!string.IsNullOrEmpty(originalPath))
|
if (!string.IsNullOrEmpty(originalPath))
|
||||||
@ -377,7 +457,8 @@ namespace MindPowerSdk.Editor
|
|||||||
byte[] bytes = texture2D.EncodeToPNG();
|
byte[] bytes = texture2D.EncodeToPNG();
|
||||||
if (bytes != null)
|
if (bytes != null)
|
||||||
{
|
{
|
||||||
File.WriteAllBytes(texturePath.Replace("Assets", Application.dataPath), bytes);
|
File.WriteAllBytes(texturePath.Replace("Assets", Application.dataPath),
|
||||||
|
bytes);
|
||||||
AssetDatabase.ImportAsset(texturePath);
|
AssetDatabase.ImportAsset(texturePath);
|
||||||
Debug.Log($"导出材质贴图:{texturePath}");
|
Debug.Log($"导出材质贴图:{texturePath}");
|
||||||
}
|
}
|
||||||
@ -402,7 +483,7 @@ namespace MindPowerSdk.Editor
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 根据传入的类型名称(如“草”或“树”)进行模糊匹配,
|
/// 根据传入的类型名称(如"草"或"树")进行模糊匹配,
|
||||||
/// 遍历材质映射配置,返回第一个匹配的目标材质,未匹配返回 null。
|
/// 遍历材质映射配置,返回第一个匹配的目标材质,未匹配返回 null。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Material GetMappedMaterial(string typeName)
|
public Material GetMappedMaterial(string typeName)
|
||||||
|
@ -13,8 +13,8 @@ public static class TerrainGenerator
|
|||||||
/// <param name="chunkSize">每块区域的瓦片数(例如 64)</param>
|
/// <param name="chunkSize">每块区域的瓦片数(例如 64)</param>
|
||||||
/// <param name="terrainMaterial">基于自定义Shader(例如 Custom/TerrainUVBlendShader)的材质</param>
|
/// <param name="terrainMaterial">基于自定义Shader(例如 Custom/TerrainUVBlendShader)的材质</param>
|
||||||
/// <param name="parent">生成的 Terrain 父节点</param>
|
/// <param name="parent">生成的 Terrain 父节点</param>
|
||||||
/// <param name="isSmoothing"></param>
|
|
||||||
/// <param name="entryMapName"></param>
|
/// <param name="entryMapName"></param>
|
||||||
|
/// <param name="isSmoothing"></param>
|
||||||
public static List<Texture> GenTerrain(MPMap map, int chunkSize, Material terrainMaterial, Transform parent,
|
public static List<Texture> GenTerrain(MPMap map, int chunkSize, Material terrainMaterial, Transform parent,
|
||||||
string entryMapName, bool isSmoothing = true)
|
string entryMapName, bool isSmoothing = true)
|
||||||
{
|
{
|
||||||
@ -26,94 +26,198 @@ public static class TerrainGenerator
|
|||||||
int yCnt = Mathf.CeilToInt((float)map.Height / chunkSize);
|
int yCnt = Mathf.CeilToInt((float)map.Height / chunkSize);
|
||||||
var terrainMap = new GameObject(entryMapName);
|
var terrainMap = new GameObject(entryMapName);
|
||||||
terrainMap.transform.parent = parent;
|
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 i = 0; i < yCnt; i++)
|
||||||
{
|
{
|
||||||
for (int j = 0; j < xCnt; j++)
|
for (int j = 0; j < xCnt; j++)
|
||||||
{
|
{
|
||||||
int startX = j * chunkSize;
|
int startX = j * chunkSize;
|
||||||
int startY = i * chunkSize;
|
int startY = i * chunkSize;
|
||||||
// 为保证边界内不越界,右侧和上侧减1
|
// 修复:正确计算边界,确保最后一块包含所有剩余像素
|
||||||
int endX = Mathf.Min(startX + chunkSize, map.Width - 1);
|
int endX = Mathf.Min(startX + chunkSize, map.Width);
|
||||||
int endY = Mathf.Min(startY + chunkSize, map.Height - 1);
|
int endY = Mathf.Min(startY + chunkSize, map.Height);
|
||||||
int resX = (endX - startX) + 1; // 横向瓦片数
|
int resX = endX - startX; // 横向瓦片数
|
||||||
int resY = (endY - startY) + 1; // 纵向瓦片数
|
int resY = endY - startY; // 纵向瓦片数
|
||||||
|
|
||||||
|
Debug.Log($"地形块[{i},{j}]: startX={startX}, startY={startY}, endX={endX}, endY={endY}, resX={resX}, resY={resY}");
|
||||||
|
|
||||||
// 为了保证生成的 Terrain 与原始地图高度一致,并且位置正确,需要使用固定分辨率
|
// 优化:使用合理的分辨率,避免过高的分辨率导致性能和兼容性问题
|
||||||
int newRes = 4097;
|
// 根据chunk大小动态计算分辨率,确保每个tile至少有4个像素
|
||||||
|
int newRes = Mathf.NextPowerOfTwo(Mathf.Max(resX, resY) * 4) + 1;
|
||||||
|
newRes = Mathf.Clamp(newRes, 129, 1025); // 限制在合理范围内
|
||||||
|
|
||||||
// 创建 TerrainData,设置高度图和混合图分辨率以及尺寸
|
// 创建 TerrainData,设置高度图和混合图分辨率以及尺寸
|
||||||
TerrainData terrainData = new TerrainData();
|
TerrainData terrainData = new TerrainData();
|
||||||
terrainData.heightmapResolution = newRes;
|
terrainData.heightmapResolution = newRes;
|
||||||
terrainData.alphamapResolution = 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])
|
// 生成高度图(归一化到 [0,1])
|
||||||
float[,] heights = GenerateHeightMap(map, startX, startY, resX, resY, newRes, globalMin, globalMax,
|
float[,] heights = GenerateHeightMap(map, startX, startY, resX, resY, newRes, globalMin, globalMax,
|
||||||
isSmoothing);
|
isSmoothing);
|
||||||
terrainData.SetHeights(0, 0, heights);
|
terrainData.SetHeights(0, 0, heights);
|
||||||
|
|
||||||
// ---------------------- 新增部分 -------------------------
|
// ---------------------- 贴图设置部分 -------------------------
|
||||||
// 生成贴图编号和遮罩贴图(调用你已有的 GenTxtNoTexture 方法)
|
// 生成贴图编号和遮罩贴图
|
||||||
Debug.LogWarning($"{startX} {startY} {chunkSize}");
|
// 注意:贴图采样使用原始的resX, resY (不包含+1边界)
|
||||||
(Texture2D texNo, Texture2D maskNo) = map.GenTxtNoTexture((short)startX, (short)startY, chunkSize);
|
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(texNo);
|
||||||
textureList.Add(maskNo);
|
textureList.Add(maskNo);
|
||||||
// 设置每个 tile 在辅助 baked UV 贴图中希望的像素尺寸(建议不小于 4)
|
if (terrainMaterial== null)
|
||||||
int cellPixelSize = 7;
|
{
|
||||||
|
Debug.LogError("地形材质未设置,请提供一个自定义材质或使用内置材质系统。");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
// 创建材质实例,并设置贴图
|
// 创建材质实例,并设置贴图
|
||||||
Material matInstance = new Material(terrainMaterial);
|
Material matInstance = new Material(terrainMaterial);
|
||||||
matInstance.SetTexture("_TexNo", texNo);
|
matInstance.SetTexture("_TexNo", texNo);
|
||||||
matInstance.SetTexture("_MaskNo", maskNo);
|
matInstance.SetTexture("_MaskNo", maskNo);
|
||||||
|
|
||||||
|
// 计算每个 tile 在 UV 空间内的尺寸
|
||||||
// 计算每个 tile 在 UV 空间内的尺寸(通常为 1/chunkSize)
|
Vector2 mainTileScale = new Vector2(1.0f / texResX, 1.0f / texResY);
|
||||||
Vector2 mainTileScale = new Vector2(1.0f / chunkSize, 1.0f / chunkSize);
|
|
||||||
matInstance.SetVector("_MainTileOffset", new Vector4(mainTileScale.x, mainTileScale.y, 0, 0));
|
matInstance.SetVector("_MainTileOffset", new Vector4(mainTileScale.x, mainTileScale.y, 0, 0));
|
||||||
|
|
||||||
// 设置 Terrain 尺寸参数,Shader 内部可根据该参数计算全局 UV0(X:宽度,Z:高度)
|
// 设置 Terrain 尺寸参数
|
||||||
matInstance.SetVector("_TerrainSize", new Vector4((resX - 1), (resY - 1), 0, 0));
|
matInstance.SetVector("_TerrainSize", new Vector4(resX, resY, 0, 0));
|
||||||
|
matInstance.SetFloat("_Repeat", resX);
|
||||||
|
// ---------------------- 贴图设置结束 -------------------------
|
||||||
|
|
||||||
matInstance.SetFloat("_Repeat", (resX - 1));
|
// 生成 Terrain 游戏对象并设置位置
|
||||||
// ---------------------- 新增部分结束 -------------------------
|
|
||||||
|
|
||||||
// 生成 Terrain 游戏对象并设置位置(Y 坐标偏移 globalMin)
|
|
||||||
GameObject terrainGO = Terrain.CreateTerrainGameObject(terrainData);
|
GameObject terrainGO = Terrain.CreateTerrainGameObject(terrainData);
|
||||||
terrainGO.name = $"terrain_{i}_{j}";
|
terrainGO.name = $"terrain_{i}_{j}";
|
||||||
terrainGO.transform.parent = terrainMap.transform;
|
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>();
|
Terrain terrainComponent = terrainGO.GetComponent<Terrain>();
|
||||||
terrainComponent.materialType = Terrain.MaterialType.Custom;
|
TerrainCollider terrainCollider = terrainGO.GetComponent<TerrainCollider>();
|
||||||
terrainComponent.materialTemplate = matInstance;
|
|
||||||
|
// 设置地形渲染和性能参数
|
||||||
|
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;
|
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,
|
private static float[,] GenerateHeightMap(MPMap map, int startX, int startY, int resX, int resY, int newRes,
|
||||||
float globalMin, float globalMax, bool isSmoothing)
|
float globalMin, float globalMax, bool isSmoothing)
|
||||||
{
|
{
|
||||||
// 采集原始高度数据,并归一化到 [0,1]
|
// 采集原始高度数据,并归一化到 [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 worldX = startX + x;
|
||||||
int worldY = startY + z;
|
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);
|
MPTile tile = map.GetTile(worldX, worldY);
|
||||||
float norm = (globalMax - globalMin) > 0 ? (tile.Height - globalMin) / (globalMax - globalMin) : 0f;
|
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[,] resampled = new float[newRes, newRes];
|
||||||
|
Loading…
x
Reference in New Issue
Block a user