/// <summary> /// 解析地形Tile数据 /// </summary> /// <param name="iSaveDir">保存目录</param> /// <param name="iTileIndex">Tile横向索引(Z轴方向)</param> /// <param name="iTerrainObj">拆分用地形对象</param> /// <param name="iMaxLayerLevel">地形最大层级</param> /// <param name="iLayerIndex">层级索引</param> /// <param name="iUpdateProgress">进度更新委托</param> public void SplitTerrains( string iSaveDir, GameObject iTerrainObj, Vector2 iTileIndex, int iMaxLayerLevel, int iLayerIndex, ProgressBarUpdate iUpdateProgress = null) { // 计算当前层级的拆分数量 var slicingCount = TerrainsSettings.CurSlicingCount(iMaxLayerLevel, iLayerIndex); // 计算当前层级Chunk尺寸计算 for (var r = 0; r < slicingCount; ++r) { for (var c = 0; c < slicingCount; ++c) { // 开始拆分 SplitTerrains( iSaveDir, iMaxLayerLevel - iLayerIndex, iTileIndex, slicingCount, iTerrainObj, new Vector2(r, c), iUpdateProgress); } } }
/// <summary> /// 拆分地形 /// </summary> /// <param name="iSaveDir">保存路径</param> /// <param name="iLayerLevel">地形层级</param> /// <param name="iTileIndex">地形的Tile索引</param> /// <param name="iSlicingCount">单个地形的拆分个数。(如:2x2 则为2)</param> /// <param name="iTerrainObj">地形对象</param> /// <param name="iChunkTileIndex">单个地形的拆分成chunk后的tile索引</param> /// <param name="iUpdateProgress">进度更新委托</param> private void SplitTerrains( string iSaveDir, int iLayerLevel, Vector2 iTileIndex, int iSlicingCount, GameObject iTerrainObj, Vector2 iChunkTileIndex, ProgressBarUpdate iUpdateProgress = null) { if (string.IsNullOrEmpty(iSaveDir)) { return; } if (0 >= iSlicingCount) { return; } if (null == iTerrainObj) { Error("SplitTerrains():The gameobject is null or invalid in hierarchy!!!"); return; } var terrain = iTerrainObj.GetComponent <Terrain>(); if (null == terrain) { Error("SplitTerrains():There is no component named terrain in the gameobject(name:{0})!!!", iTerrainObj.name); return; } // 地形数据 var terrainData = terrain.terrainData; // 地形尺寸 var terrainSize = terrainData.size; // 得到地图分辨率 var newHeightmapResolution = (terrainData.heightmapResolution - 1) / iSlicingCount; var newAlphamapResolution = terrainData.alphamapResolution / iSlicingCount; var newbaseMapResolution = terrainData.baseMapResolution / iSlicingCount; // 溅斑原型列表 var splatProtos = terrainData.splatPrototypes; var x = (int)iChunkTileIndex.x; var y = (int)iChunkTileIndex.y; //创建资源 // 地形拆分chunk保存路径 var morton = TerrainsSettings.Morton(iTileIndex, iChunkTileIndex, iSlicingCount, iLayerLevel); var chunkName = string.Format("{0}{1}.asset", TerrainsSettings.ChunkNamePrefix, morton); var savePath = string.Format("{0}/{1}", iSaveDir, chunkName); // 更新进度 - 拆分开始 if (null != iUpdateProgress) { var statusTxt = string.Format("拆分开始:{0}...", chunkName); iUpdateProgress(statusTxt, false); } var newData = new TerrainData(); if (null == newData) { Error("SplitTerrains():Failed!!(SavePath:{0})", savePath); return; } AssetDatabase.CreateAsset(newData, savePath); //设置分辨率参数 newData.heightmapResolution = newHeightmapResolution; newData.alphamapResolution = newAlphamapResolution; newData.baseMapResolution = newbaseMapResolution; //设置大小 newData.size = new Vector3(terrainSize.x / iSlicingCount, terrainSize.y, terrainSize.z / iSlicingCount); //设置地形原型 var newSplats = new SplatPrototype[splatProtos.Length]; for (var i = 0; i < splatProtos.Length; ++i) { newSplats[i] = new SplatPrototype { texture = splatProtos[i].texture, tileSize = splatProtos[i].tileSize }; var offsetX = (newData.size.x * x) % splatProtos[i].tileSize.x + splatProtos[i].tileOffset.x; var offsetY = (newData.size.z * y) % splatProtos[i].tileSize.y + splatProtos[i].tileOffset.y; newSplats[i].tileOffset = new Vector2(offsetX, offsetY); } newData.splatPrototypes = newSplats; //设置混合贴图 var alphamap = new float[newAlphamapResolution, newAlphamapResolution, splatProtos.Length]; alphamap = terrainData.GetAlphamaps( x * newData.alphamapWidth, y * newData.alphamapHeight, newData.alphamapWidth, newData.alphamapHeight); newData.SetAlphamaps(0, 0, alphamap); //设置高度 var xBase = (terrainData.heightmapWidth - 1) / iSlicingCount; var yBase = (terrainData.heightmapHeight - 1) / iSlicingCount; var height = terrainData.GetHeights(xBase * x, yBase * y, xBase + 1, yBase + 1); newData.SetHeights(0, 0, height); AssetDatabase.SaveAssets(); // 更新进度 - 拆分成功 if (null != iUpdateProgress) { var statusTxt = string.Format("拆分成功:{0}", chunkName); iUpdateProgress(statusTxt, true); } }
/// <summary> /// 拆分地形 /// </summary> /// <param name="iLevel">层级(影响到左边缩进的余白)</param> private void SplitTerrains(int iLevel) { var level = iLevel; DrawMiddleGameObject( level, "地形根节点:", ref _terrainsRoot); var childCount = 0; if (!_terrainsRoot) { return; } childCount = _terrainsRoot.transform.childCount; // 若长宽地形个数不想等 if (false == UtilsTools.IsSquare(childCount)) { DrawLabel(level + 1, "地形部署长宽不相同(不是 N x N 布局)!", Color.red); } else { // 保存地形Tile尺寸 if (Math.Abs(ConfData.TileSize.x * ConfData.TileSize.y - childCount) > 0.0f) { ConfData.TileSize.x = (float)Math.Sqrt(childCount); ConfData.TileSize.y = ConfData.TileSize.x; } DrawMiddleLabel(level, "chunk拆分数:"); ++level; DrawLabel(level, "因为与到处高度图相关,拆分个数必须为2的N次幂(1~64)", Color.yellow); var lastSlicingCount = ConfData.chunkData.SlicingCount; DrawMiddleSlider( level, "个数( N x N ):", ref lastSlicingCount, 1.0f, 64); if (!UtilsTools.IsPowerOf2((int)lastSlicingCount)) { var error = string.Format("{0} 不是2的N次幂(1~64)!", (int)ConfData.chunkData.SlicingCount); DrawLabel(level + 1, error, Color.red); } else { if (Math.Abs(lastSlicingCount - ConfData.chunkData.SlicingCount) > 0.0f) { // 有变化贼,晴空已有数据 Settings.Clear(); ConfData.chunkData.SlicingCount = lastSlicingCount; // 清空Debug用绘制节点 ClearDebugNode(); // 层级预览信息清空 _layerPreviewTitles.Clear(); _layerPreviewSelected.Clear(); } // 分界线 DrawLongLabel(0, "---------------"); DrawSingleLongToggle(level, "是否预览拆分边界:", _chunkBoundPreviewVisiable, delegate(bool iValue) { _chunkBoundPreviewVisiable = iValue; }); // 选择层级 // 取得最高层级 var maxLayerLevel = GetMaxLayerLevel((int)ConfData.chunkData.SlicingCount); // 层级菜单标题 if (0 >= _layerPreviewSelected.Count) { for (var l = 0; l <= maxLayerLevel; ++l) { _layerPreviewTitles.Add(string.Format("层级{0}", l)); _layerPreviewSelected.Add(false); } } if (_chunkBoundPreviewVisiable) { // 各层级显示 DrawLongLabel(level + 1, string.Format("当前拆分数可分 {0} 层:", maxLayerLevel + 1)); DrawMiddleMultiplyToggle(level + 2, _layerPreviewTitles.ToArray(), _layerPreviewSelected.ToArray(), delegate(int l, bool iSelected) { _layerPreviewSelected[l] = iSelected; if (!iSelected) { ClearDebugNode(l); } }); // 显示颜色设定 var flg = false; for (var l = 0; l <= maxLayerLevel; ++l) { if (!_layerPreviewSelected[l]) { continue; } flg = true; } if (flg) { var layerPreview = ConfData.chunkData.Preview; DrawLongLabel(level + 3, "颜色设定:"); DrawMiddleColor(level + 4, "边色", layerPreview.edgeColor, delegate(Color iColor) { layerPreview.edgeColor = iColor; // 重置所有边的颜色 UtilsDraw.ResetAllCylinderColor(iColor, (int)DrawType.TerrainChunkCylinder); }); DrawMiddleColor(level + 4, "顶点色", layerPreview.vertexColor, delegate(Color iColor) { layerPreview.vertexColor = iColor; // 重置所有边的颜色 UtilsDraw.ResetAllVertexColor(iColor, (int)DrawType.TerrainChunkVertex); }); DrawMiddleSlider(level + 4, "缩放", layerPreview.Scale, delegate(float iValue) { layerPreview.Scale = iValue; UtilsDraw.ResetAllBoundsPreviewScale( iValue, (int)DrawType.TerrainChunkCylinder | (int)DrawType.TerrainChunkVertex); }, 1.0f, 50.0f); --level; } // 遍历根节点并取得包围盒 if (null == _terrainsRoot) { return; } // 基准原点:子节点中最小的点为为基准原点(既:最左最前的为第一块地形Tile) // 其他的地形都以该地形位置准排布 var baseOriginPos = GetOriginPosOfTerrainsFromTiles(_terrainsRoot.transform); var terrainTileSize = Vector3.zero; for (var i = 0; i < _terrainsRoot.transform.childCount; ++i) { var child = _terrainsRoot.transform.GetChild(i); if (null == child) { continue; } if (false == child.gameObject.activeInHierarchy) { continue; } // 地形Tile尺寸 if (Vector3.zero.Equals(terrainTileSize)) { terrainTileSize = TerrainsSettings.GetTerrainSize(child.gameObject); } // Tile Index var tileIndex = GetTerrainTileIndexByPos( baseOriginPos, child.transform.position, terrainTileSize); // 按照层级生成解析用的地形层级数据 for (var layerIndex = 0; layerIndex <= maxLayerLevel; ++layerIndex) { // 当前层级的chunk尺寸 var layerChunkSize = TerrainsSettings.GetTerrainChunkSize( terrainTileSize, maxLayerLevel, layerIndex); // 拆分当前层级的chunk数据 Settings.ParserTerrainTileData( tileIndex, layerChunkSize, maxLayerLevel, layerIndex); } for (var l = 0; l <= maxLayerLevel; ++l) { if (!_layerPreviewSelected[l]) { continue; } // 取得当前层级的chunk信息 var chunks = Settings.GetChunkData(l); if (null == chunks || 0 >= chunks.Count) { continue; } var bounds = new List <Bounds>(); // 以莫顿码为chunk索引 var chunksIndex = new List <int>(); foreach (var chunk in chunks) { bounds.Add(chunk.Bounds); chunksIndex.Add((int)chunk.Morton); } // 绘制Chunks包围盒 var layerPreview = ConfData.chunkData.Preview; if (null == layerPreview) { continue; } UtilsDraw.DrawChunkBounds( bounds.ToArray(), chunksIndex.ToArray(), child.transform, layerPreview.edgeColor, layerPreview.vertexColor, layerPreview.Scale, l); } } Settings.AutoSort(); } else { ClearDebugNode(); } DrawLongLabel(0, "---------------"); DrawLongLabel(0, "拆分设定:"); DrawLongLabel(1, "数据导入/导出目录:"); DrawLabel(2, string.Format("Chunk : {0}", Settings.ChunkDataDir), Color.green); DrawLabel(2, string.Format("预制体 : {0}", Settings.PrefabsDataDir), Color.green); DrawSingleMiddleToggle(1, "仅处理静态物体:", ConfData.chunkData.StaticOnly, delegate(bool iValue) { ConfData.chunkData.StaticOnly = iValue; }); // 地形导出层级 DrawSelectList(1, "地形导出层级:", _layerPreviewTitles.ToArray(), ref _exportLayerLevel); // 预制体导出深度 DrawMiddleSlider(1, "预制体导出深度:", ConfData.chunkData.PrefabsExportDepthLimit, delegate(float iValue) { ConfData.chunkData.PrefabsExportDepthLimit = (int)iValue; }, 1.0f, 15.0f); DrawSingleButton(0, "拆分场景", delegate() { // 显示处理进度条 ProgressBarStart("拆分地形", "开始拆分..."); // Chunk导出目录检测 var dirInfo = new DirectoryInfo(Settings.ChunkDataDir); UtilsTools.CheckAndCreateDirByFullDir(dirInfo.FullName); // 清空保存目录,但不删除目录本身 UtilsTools.ClearDirectory(TerrainsSettings.DataBaseDir, false); // 遍历根节点并取得包围盒 if (null == _terrainsRoot) { return; } // 基准原点:子节点中最小的点为为基准原点(既:最左最前的为第一块地形Tile) // 其他的地形都以该地形位置准排布 var baseOriginPos = GetOriginPosOfTerrainsFromTiles(_terrainsRoot.transform); var terrainTileSize = Vector3.zero; var maxCount = _terrainsRoot.transform.childCount * ConfData.chunkData.SlicingCount * ConfData.chunkData.SlicingCount; var progressCount = 0; for (var i = 0; i < _terrainsRoot.transform.childCount; ++i) { var child = _terrainsRoot.transform.GetChild(i); if (null == child) { continue; } if (false == child.gameObject.activeInHierarchy) { continue; } // 地形Tile尺寸 if (Vector3.zero.Equals(terrainTileSize)) { terrainTileSize = TerrainsSettings.GetTerrainSize(child.gameObject); } // Tile Index var tileIndex = GetTerrainTileIndexByPos( baseOriginPos, child.transform.position, terrainTileSize); // 拆分地形 TerrainsSplit.Instance.SplitTerrains( Settings.ChunkDataDir, child.gameObject, tileIndex, maxLayerLevel, maxLayerLevel - _exportLayerLevel, delegate(string iStatusTxt, bool iStatusCount) { if (iStatusCount) { ++progressCount; } // 计算进度 var progress = progressCount / maxCount; ProgressBarUpdate(iStatusTxt, progress); this.Info("SplitTerrains:({0}) {1}", progress, iStatusTxt); }); } // 初始化地形拆分用的预制体信息 TerrainsSplit.Instance.InitPrefabsInfo( ConfData.chunkData.StaticOnly, ConfData.chunkData.PrefabsExportDepthLimit); ProgressBarUpdate( "地形拆分成功", 1.0f); // 到处此次地形拆分配置信息 TerrainsSettings.GetInstance().ExportBySceneName(CurSceneName); ProgressBarClear(); // 刷新 AssetDatabase.Refresh(); }, Color.yellow); } } }