Main/Assets/Editor/SplitTerrain/CollectThings.cs

355 lines
12 KiB
C#
Raw Permalink Normal View History

2025-01-25 04:38:09 +08:00
using Thousandto.Core.Asset;
using System.Collections.Generic;
using System.IO;
using System.Text;
using UnityEditor;
using UnityEditor.SceneManagement;
using UnityEngine;
using UnityEngine.SceneManagement;
namespace Thousandto.Editor.Test
{
public enum CollectTypeDefine
{
SkyBox, //天空盒子
Grass, //草
Effect, //场景特效
Mass, //其他杂七杂八的物件
}
public struct Thing
{
public string Name { get; set; }
public CollectTypeDefine CType { get; set; }
public GameObject Go { get; set; }
//当前物件的边界信息
public Bounds Bounds { get; set; }
//网格中的索引
public int Index { get; set; }
};
/// <summary>
/// 收集场景中的物件以分块区域来划分生成新的prefab
/// </summary>
public class CollectThings
{
List<MyTerrainData> _allTerrainDatas = new List<MyTerrainData>();
List<Thing> _grassThings = new List<Thing>();
List<Thing> _skyBoxThings = new List<Thing>();
List<Thing> _effectThings = new List<Thing>();
//跨mesh的普通物件
List<Thing> _otherThings = new List<Thing>();
//一些没有跨mesh存在的物件则集中存放到一起保存成一个新的prefab
Dictionary<MyTerrainData, List<Thing>> _thingInOneTerrainDict = new Dictionary<MyTerrainData, List<Thing>>();
private string _curMapName = "";
//记录各个分块
Dictionary<int, Rect> _resolutionDict = new Dictionary<int, Rect>();
//<index, <thingType, thingList>>
Dictionary<int, Dictionary<int, List<Thing>>> _collectThingDict = new Dictionary<int, Dictionary<int, List<Thing>>>();
private int _mapSize = 0;
private int _blockSize = 0;
/// <summary>
/// 设置分辨率
/// </summary>
/// <param name="mapSize">地图大小</param>
/// <param name="blockSize">分块大小</param>
public void SetResolution(int mapSize, int blockSize)
{
_mapSize = mapSize;
_blockSize = blockSize;
float countf = mapSize / (float)blockSize;
int count = mapSize / blockSize;
if (countf - count > 0.1f)
count++;
_resolutionDict.Clear();
for (int i = 0; i < count; ++i)
{
for(int j = 0; j < count; ++j)
{
Rect rect = new Rect(i * blockSize, j * blockSize, blockSize, blockSize);
int key = TerrainConfigLoader.CalcIndex(i, j);
_resolutionDict.Add(key, rect);
}
}
}
public void Collect(string mapName = "")
{
if(string.IsNullOrEmpty(mapName))
{
var path = AssetDatabase.GetAssetPath(Selection.activeObject);
if (File.Exists(path))
{
EditorSceneManager.OpenScene(path);
}
}
else
{
if(mapName != EditorSceneManager.GetActiveScene().name)
{
var path = ConfigDefine.GetSceneFile(mapName);
if (File.Exists(path))
EditorSceneManager.OpenScene(path);
}
}
//删除旧的thing prefab
DeleteOldThingPrefabs();
var scene = EditorSceneManager.GetActiveScene();
var name = scene.name;
_curMapName = name;
_allTerrainDatas = LoadAllSplitedTerrain(name);
if (!CollectThingsInRoot())
return;
AssetDatabase.StartAssetEditing();
var configList = SaveThings(_collectThingDict);
AssetDatabase.StopAssetEditing();
if(configList.Count > 0)
{
var savePath = ConfigDefine.GetConfigPath(TerrainConfigDefine.THING_CONFIG_FILENAME);
File.WriteAllLines(savePath, configList.ToArray());
}
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
}
public static List<MyTerrainData> LoadAllSplitedTerrain(string mapName)
{
List<MyTerrainData> list = new List<MyTerrainData>();
string storePath = ConfigDefine.GetSaveTerrainPath(mapName);
if (!Directory.Exists(storePath))
{
Utils.ShowErrorMsgBox("加载小块地图网格", "找不到存储路径:" + storePath);
return list;
}
var fileList = Directory.GetFiles(storePath, "*.obj");
for(int i = 0; i < fileList.Length; ++i)
{
list.Add(MyTerrainData.CreateTerrainDataFromFileName(fileList[i]));
}
return list;
}
public bool CollectThingsInRoot(string root = "SceneRoot")
{
var rootGo = GameObject.Find(root);
if(rootGo == null)
{
Utils.ShowErrorMsgBox("获取场景中所有GameObject", "没有找到跟节点SceneRoot");
return false;
}
List<GameObject> gos = new List<GameObject>();
FindGameObjects(rootGo, gos);
for(int i = 0; i < gos.Count; ++i)
{
string fullName = Utils.GetGameObjectHierarchyPath(gos[i]);
if (gos[i].transform.childCount > 0 && gos[i].transform.position == Vector3.zero && gos[i].transform.localScale == Vector3.one)
continue;
Thing thing = new Thing();
thing.CType = GetThingTypeFromName(fullName);
thing.Go = gos[i];
thing.Name = fullName;
thing.Bounds = GetThingBounds(gos[i]);
//thing.RelateToMeshs = CalcRelativeToTerrainMeshes(thing, _allTerrainDatas);
if (thing.Bounds.size == Vector3.zero)
thing.Index = Utils.CalcIndex(thing.Go.transform.position, _blockSize);
else
thing.Index = Utils.CalcIndex(Utils.GetGameObjectCenter(thing), _blockSize);
Dictionary<int, List<Thing>> typeToThing;
if(!_collectThingDict.TryGetValue(thing.Index, out typeToThing))
{
typeToThing = new Dictionary<int, List<Thing>>();
_collectThingDict.Add(thing.Index, typeToThing);
}
if(thing.CType != CollectTypeDefine.SkyBox)
{
Utils.AddToDict(typeToThing, (int)thing.CType, thing);
}
else
{
_skyBoxThings.Add(thing);
}
}
return true;
}
private List<string> SaveThings(Dictionary<int, Dictionary<int, List<Thing>>> dataDict)
{
CreateNewPrefab("skybox", _skyBoxThings);
List<string> configList = new List<string>();
var itorRoot = dataDict.GetEnumerator();
while(itorRoot.MoveNext())
{
var rootGo = new GameObject("Thing_" + itorRoot.Current.Key);
var itor = itorRoot.Current.Value.GetEnumerator();
while(itor.MoveNext())
{
var rootName = GetRootName(itor.Current.Key);
var thingList = itor.Current.Value;
if(thingList != null)
{
var go = CopyToGameObject(rootName, thingList);
go.transform.parent = rootGo.transform;
}
}
string savePath = ConfigDefine.GetSavePrefabPath(rootGo.name + ".prefab");
configList.Add(string.Format("{0}", itorRoot.Current.Key));
PrefabUtility.CreatePrefab(savePath, rootGo);
AssetDatabase.ImportAsset(savePath);
GameObject.DestroyImmediate(rootGo);
}
EditorUtility.ClearProgressBar();
return configList;
}
private string GetRootName(int thingType)
{
CollectTypeDefine cType = (CollectTypeDefine)thingType;
switch(cType)
{
case CollectTypeDefine.Effect:
return "scene_effect";
case CollectTypeDefine.Grass:
return "grass";
case CollectTypeDefine.SkyBox:
return "skybox";
}
return "default";
}
private GameObject CopyToGameObject(string rootName, List<Thing> things)
{
GameObject go = new GameObject(rootName);
for (int i = 0; i < things.Count; ++i)
{
var inst = GameObject.Instantiate(things[i].Go);
inst.name = things[i].Go.name;
inst.transform.parent = go.transform;
Utils.CopyTransProperty(inst.transform, things[i].Go.transform);
Utils.CopyMesh(inst.transform, things[i].Go.transform);
}
return go;
}
private string CreateNewPrefab(string rootName, List<Thing> things)
{
GameObject go = new GameObject(rootName);
for(int i = 0; i < things.Count; ++i)
{
var inst = GameObject.Instantiate(things[i].Go);
inst.name = things[i].Go.name;
inst.transform.parent = go.transform;
Utils.CopyTransProperty(inst.transform, things[i].Go.transform);
Utils.CopyMesh(inst.transform, things[i].Go.transform);
}
string savePath = ConfigDefine.GetSavePrefabPath(rootName + ".prefab");
PrefabUtility.CreatePrefab(savePath, go);
AssetDatabase.ImportAsset(savePath);
GameObject.DestroyImmediate(go);
return savePath;
}
private void FindGameObjects(GameObject root, List<GameObject> refList)
{
List<GameObject> retList = refList;
Transform rootTran = root.transform;
if (rootTran.childCount == 0)
{
retList.Add(rootTran.gameObject);
return;
}
for (int i = 0; i < rootTran.childCount; ++i)
{
var temp = rootTran.GetChild(i);
{
FindGameObjects(temp.gameObject, refList);
}
}
}
private static CollectTypeDefine GetThingTypeFromName(string name)
{
if (name.IndexOf("sky", System.StringComparison.CurrentCultureIgnoreCase) >= 0)
return CollectTypeDefine.SkyBox;
if (name.IndexOf("grass", System.StringComparison.CurrentCultureIgnoreCase) >= 0)
return CollectTypeDefine.Grass;
if (name.IndexOf("effect", System.StringComparison.CurrentCultureIgnoreCase) >= 0)
return CollectTypeDefine.Effect;
return CollectTypeDefine.Mass;
}
private static bool IsBoxcollider(GameObject go)
{
return go.GetComponentInChildren<BoxCollider>() != null;
}
private static Bounds GetThingBounds(GameObject go)
{
var mf = go.GetComponentInChildren<MeshFilter>();
if (mf == null || mf.sharedMesh == null)
{
var boxcollider = go.GetComponentInChildren<BoxCollider>();
if (boxcollider == null)
{
Transform trans = go.transform;
Bounds bounds = new Bounds(trans.position, Vector3.zero);
return bounds;
}
//boxcollider.bounds是世界坐标这里需要相对坐标
return new Bounds(boxcollider.center, boxcollider.size);
}
return mf.sharedMesh.bounds;
}
private static void DeleteOldThingPrefabs()
{
var path = ConfigDefine.GetSavePrefabPath();
var fileList = Directory.GetFiles(path, "*.prefab");
for(int i = 0; i < fileList.Length; ++i)
{
if (fileList[i].StartsWith("Thing_"))
File.Delete(fileList[i]);
}
}
}
}