843 lines
33 KiB
C#
843 lines
33 KiB
C#
// Scene材质会单独加载Bundle,一般物体则按GameObject流程加载。
|
||
// Scene被分解后,非平行光和其他非运行时物体都直接抛弃;Renderer材质全部修改为白模;
|
||
// 运行时,按照九宫格流程加载所需资源;加载完成材质后,替换当前Renderer中的白模材质;
|
||
// 模型网格会按照场景需求进行复制;材质、着色器和贴图会按照独一无二方式处理;
|
||
|
||
using System;
|
||
using System.Collections.Generic;
|
||
using System.IO;
|
||
using System.Linq;
|
||
using System.Security.Cryptography;
|
||
using System.Text;
|
||
using Games.Scene;
|
||
using GCGame.Table;
|
||
using NavMeshExtension;
|
||
using UnityEditor;
|
||
using UnityEditor.SceneManagement;
|
||
using UnityEngine;
|
||
using UnityEngine.Events;
|
||
using UnityEngine.Rendering;
|
||
using UnityEngine.SceneManagement;
|
||
using Object = UnityEngine.Object;
|
||
|
||
public class SceneDisassembly : EditorPerFrameActionBase
|
||
{
|
||
public const string sceneRoot = "Assets/Res_newMS/ChangJing/Sence";
|
||
public const string sceneDisassembledPath = "Assets/Project3D/BundleData/Scene";
|
||
//public const string roleSelectDir = sceneRoot + "/"; //+ GlobeVar.sceneRoleSelect;
|
||
//public const string roleSelectTargetDir = sceneDisassembledPath + "/" + GlobeVar.sceneRoleSelect;
|
||
|
||
public const string itemRootLight = "Lights";
|
||
public const string itemRootAnimator = "Animators";
|
||
public const string itemRootParticle = "Particles";
|
||
public const string itemRootSceneObj = "SceneObjs";
|
||
public const string itemRootCollider = "OtherColliders";
|
||
public const string itemRootMisc = "MiscItems";
|
||
public const string sceneExtension = ".unity";
|
||
|
||
private readonly List<GameObject> _parentGameObjects = new List<GameObject>();
|
||
private readonly string _sceneAssetPath;
|
||
|
||
private readonly string _sceneName;
|
||
private readonly string _targetAssetPath;
|
||
|
||
private SceneDisassemblyProcess _index;
|
||
private Scene _newScene;
|
||
private Scene _oldScene;
|
||
private Tab_SceneClass _sceneClass;
|
||
|
||
public SceneDisassembly(string sceneAssetPath)
|
||
{
|
||
_index = SceneDisassemblyProcess.End;
|
||
var fullSceneName = Path.GetFileName(sceneAssetPath);
|
||
_sceneName = Path.GetFileNameWithoutExtension(fullSceneName);
|
||
_sceneAssetPath = sceneAssetPath;
|
||
_targetAssetPath = sceneDisassembledPath.Open(fullSceneName);
|
||
_index = 0;
|
||
}
|
||
|
||
protected override int GetCurrentIndex()
|
||
{
|
||
return (int) _index;
|
||
}
|
||
|
||
protected override int GetTotalCount()
|
||
{
|
||
return (int) SceneDisassemblyProcess.End;
|
||
}
|
||
|
||
protected override void PerFrameAction()
|
||
{
|
||
switch (_index)
|
||
{
|
||
case SceneDisassemblyProcess.Open:
|
||
// 异常防御机制
|
||
var temp = _index;
|
||
_index = SceneDisassemblyProcess.End;
|
||
var sceneClass = EditorTableManager.GetTable<Tab_SceneClass>();
|
||
_sceneClass = sceneClass.Find(a => a.ResName.Equals(_sceneName, StringComparison.OrdinalIgnoreCase));
|
||
if (_sceneClass != null)
|
||
{
|
||
// 检查场景Hash,如果匹配就不执行分解
|
||
var lastHash = GetLastSceneHash(_sceneAssetPath);
|
||
var currentHash = GetCurrentScenePath(_sceneAssetPath);
|
||
if (lastHash != currentHash)
|
||
{
|
||
_oldScene = EditorSceneManager.OpenScene(_sceneAssetPath, OpenSceneMode.Single);
|
||
if (_oldScene.isLoaded)
|
||
_index = temp + 1;
|
||
else
|
||
Debug.LogError("场景加载失败 " + _sceneName);
|
||
}
|
||
else
|
||
Debug.LogWarning("场景 " + _sceneName + " Hash匹配,跳过分解流程!");
|
||
}
|
||
else
|
||
{
|
||
Debug.LogError("场景数据获得失败 " + _sceneName);
|
||
}
|
||
|
||
break;
|
||
case SceneDisassemblyProcess.Create:
|
||
_newScene = EditorSceneManager.NewScene(NewSceneSetup.EmptyScene, NewSceneMode.Additive);
|
||
_index++;
|
||
break;
|
||
case SceneDisassemblyProcess.Disassemble:
|
||
MoveSceneObjs();
|
||
SwapSceneObjs();
|
||
EditorSceneManager.CloseScene(_newScene, true);
|
||
_index++;
|
||
break;
|
||
case SceneDisassemblyProcess.CreateLightData:
|
||
// 注:确实需要保存两次,因为SceneAsset和LightingDataAsset是循环关联的
|
||
// 第一次创建场景资源;然后创建新的LightingData,LightingData关联场景资源;然后替换场景LightningData后再保存场景
|
||
EditorSceneManager.SaveScene(_oldScene, _targetAssetPath);
|
||
CreateLightingData();
|
||
_index++;
|
||
break;
|
||
case SceneDisassemblyProcess.CreateAreas:
|
||
FinalTweaks();
|
||
CheckUnexpected();
|
||
BuiltinAssetBuilder.ReplaceForOneScene(_oldScene);
|
||
RemoveAreaAssets();
|
||
_index++;
|
||
break;
|
||
case SceneDisassemblyProcess.Save:
|
||
// 分解结束后第二次保存
|
||
EditorSceneManager.SaveScene(_oldScene, _targetAssetPath);
|
||
WriteSceneHash(_sceneAssetPath);
|
||
_index++;
|
||
break;
|
||
default:
|
||
_index++;
|
||
break;
|
||
}
|
||
}
|
||
|
||
private void MoveSceneObjs()
|
||
{
|
||
// 预先移除出现故障的组件
|
||
DestroyComponentByCondition<MeshCollider>(a => a.sharedMesh == null);
|
||
DestroyComponentByCondition<Animator>(a => a.runtimeAnimatorController == null);
|
||
DestroyComponentByCondition<Light>(a => a.type != LightType.Directional);
|
||
DestroyComponentByCondition<MeshRenderer>(a =>
|
||
{
|
||
var filter = a.GetComponent<MeshFilter>();
|
||
return filter == null || filter.sharedMesh == null;
|
||
});
|
||
DestroyComponentByCondition<SkinnedMeshRenderer>(a =>
|
||
a.sharedMesh == null || a.rootBone == null || a.bones == null || a.bones.Length < 1);
|
||
DestroyComponentByCondition<T4MObjSC>(a => true);
|
||
var rootObjects = _oldScene.GetRootGameObjects();
|
||
for (var i = 0; i < rootObjects.Length; i++)
|
||
MoveGameObjectToNewScene(rootObjects[i].transform);
|
||
}
|
||
|
||
private static void TweakComponent<T>(UnityAction<T> action) where T : Component
|
||
{
|
||
var components = Object.FindObjectsOfType<T>();
|
||
for (var i = 0; i < components.Length; i++)
|
||
action.Invoke(components[i]);
|
||
}
|
||
|
||
private void SwapSceneObjs()
|
||
{
|
||
var oldRoots = _oldScene.GetRootGameObjects();
|
||
var newRoots = _newScene.GetRootGameObjects();
|
||
for (var i = 0; i < newRoots.Length; i++)
|
||
SceneManager.MoveGameObjectToScene(newRoots[i], _oldScene);
|
||
for (var i = 0; i < oldRoots.Length; i++)
|
||
Object.DestroyImmediate(oldRoots[i]);
|
||
}
|
||
|
||
private void FinalTweaks()
|
||
{
|
||
var rootItems = _oldScene.GetRootGameObjects();
|
||
for (var i = 0; i < rootItems.Length; i++)
|
||
PurgeAssets.PurgeOneGameObject(rootItems[i].transform);
|
||
// 非Unity内置材质切换到空白材质
|
||
TweakComponent<Renderer>(a =>
|
||
{
|
||
// 保留一些动画的投影能力
|
||
if (!(a is SkinnedMeshRenderer))
|
||
{
|
||
a.shadowCastingMode = ShadowCastingMode.Off;
|
||
a.receiveShadows = a is MeshRenderer;
|
||
}
|
||
a.allowOcclusionWhenDynamic = false;
|
||
a.motionVectorGenerationMode = MotionVectorGenerationMode.ForceNoMotion;
|
||
});
|
||
TweakComponent<Light>(a =>
|
||
{
|
||
a.lightmapBakeType = LightmapBakeType.Mixed;
|
||
a.shadows = LightShadows.Soft;
|
||
a.shadowResolution = LightShadowResolution.High;
|
||
//// 仅仅用于将主角影子投射到地面
|
||
a.cullingMask = LayerMask.GetMask("MainPlayer") | ActiveScene.terrainLayMask;
|
||
});
|
||
// 修复一个可能导致无动画SkinnedMeshRenderer骨骼丢失的情况 - 该情况下本身蒙皮也无法运动
|
||
TweakComponent<SkinnedMeshRenderer>(a =>
|
||
{
|
||
a.skinnedMotionVectors = false;
|
||
var error = a.rootBone == null;
|
||
if (!error)
|
||
for (var i = 0; i < a.bones.Length; i++)
|
||
if (a.bones[i] == null)
|
||
{
|
||
error = true;
|
||
break;
|
||
}
|
||
|
||
if (error)
|
||
{
|
||
a.rootBone = null;
|
||
a.bones = new Transform[0];
|
||
}
|
||
});
|
||
|
||
var lightRoot = GameObject.Find(itemRootLight);
|
||
if (lightRoot != null)
|
||
lightRoot.SetStaticRecursively(false);
|
||
var particleRoot = GameObject.Find(itemRootParticle);
|
||
if (particleRoot != null)
|
||
particleRoot.SetStaticRecursively(false);
|
||
var animatorRoot = GameObject.Find(itemRootAnimator);
|
||
if (animatorRoot != null)
|
||
animatorRoot.SetStaticRecursively(false);
|
||
var sceneObjRoot = GameObject.Find(itemRootSceneObj);
|
||
if (sceneObjRoot != null)
|
||
sceneObjRoot.SetStaticRecursively(true);
|
||
var colliderRoot = GameObject.Find(itemRootCollider);
|
||
if (colliderRoot != null)
|
||
colliderRoot.SetStaticRecursively(true);
|
||
RenderSettings.skybox = null;
|
||
// var cameraRoot = new GameObject("CameraRoot");
|
||
// var cameraObj = new GameObject("Main Camera") {tag = "MainCamera"};
|
||
// cameraObj.transform.SetParent(cameraRoot.transform);
|
||
// var camera = cameraObj.AddComponent<Camera>();
|
||
// camera.allowHDR = false;
|
||
// camera.useOcclusionCulling = false;
|
||
// camera.allowMSAA = false;
|
||
}
|
||
|
||
private void CreateLightingData()
|
||
{
|
||
var lightingData = Lightmapping.lightingDataAsset;
|
||
var sourcePath = AssetDatabase.GetAssetPath(lightingData);
|
||
var extension = Path.GetExtension(sourcePath);
|
||
var index = _targetAssetPath.LastIndexOf('.');
|
||
var lightingDataPath = index < 0 ? _targetAssetPath + extension : _targetAssetPath.Remove(index) + extension;
|
||
if (AssetDatabase.CopyAsset(sourcePath, lightingDataPath))
|
||
{
|
||
lightingData = AssetDatabase.LoadAssetAtPath<LightingDataAsset>(lightingDataPath);
|
||
var serializedObject = new SerializedObject(lightingData);
|
||
var property = serializedObject.FindProperty("m_Scene");
|
||
var newSceneAsset = AssetDatabase.LoadAssetAtPath<SceneAsset>(_targetAssetPath);
|
||
property.objectReferenceValue = newSceneAsset;
|
||
serializedObject.ApplyModifiedProperties();
|
||
EditorUtility.SetDirty(lightingData);
|
||
Lightmapping.lightingDataAsset = lightingData;
|
||
}
|
||
else
|
||
Debug.LogError("Lighting Data Asset Copy Failed!");
|
||
}
|
||
|
||
private void CheckUnexpected()
|
||
{
|
||
var components = Object.FindObjectsOfType<Component>();
|
||
for (var i = 0; i < components.Length; i++)
|
||
{
|
||
var component = components[i];
|
||
if (!(component is Renderer) &&
|
||
!(component is Transform) &&
|
||
!(component is MeshFilter) &&
|
||
!(component is Collider) &&
|
||
!(component is Camera) &&
|
||
!(component is SceneLogic) &&
|
||
!(component is Light) &&
|
||
!(component is Animator) &&
|
||
!(component is ParticleSystem) &&
|
||
!(component is FogDensityLerp) &&
|
||
!(component is DirectionalLightSetter) &&
|
||
//!(component is uv_l_r) &&
|
||
//!(component is uv_u_d) &&
|
||
!(component is SceneObjItem) &&
|
||
!(component is MovieEventRecv) &&
|
||
!(component is AddChildAsset) &&
|
||
!(component is DailyCopySceneAnimCtr) &&
|
||
!(component is LoginShow) &&
|
||
!(component is ObjectRotate))
|
||
Debug.LogError("Unexpected Component " + component.GetType() + " on item " +
|
||
component.transform.GetHierarchyName());
|
||
}
|
||
}
|
||
|
||
private string CheckComponent<T>(Transform root, string parentName) where T : Component
|
||
{
|
||
var result = root.GetComponent<T>();
|
||
if (result != null)
|
||
{
|
||
var mono = result as MonoBehaviour;
|
||
if ((mono == null || mono.enabled) && result.gameObject.activeInHierarchy)
|
||
return parentName;
|
||
}
|
||
return null;
|
||
}
|
||
|
||
private void MoveGameObjectToNewScene(Transform root)
|
||
{
|
||
if (!root.gameObject.activeInHierarchy)
|
||
return;
|
||
var moveChildren = true;
|
||
// parentName == null表示不需要移动
|
||
string parentName = null;
|
||
|
||
if (parentName == null)
|
||
{
|
||
var sceneLogic = root.GetComponent<SceneLogic>();
|
||
if (sceneLogic != null)
|
||
{
|
||
parentName = string.Empty;
|
||
moveChildren = false;
|
||
}
|
||
}
|
||
|
||
if (parentName == null)
|
||
parentName = CheckComponent<Light>(root, itemRootLight);
|
||
|
||
if (parentName == null)
|
||
{
|
||
var animator = root.GetComponent<Animator>();
|
||
if (animator != null && animator.isActiveAndEnabled)
|
||
{
|
||
// 特殊处理带有Animator的物体,不能分解物体本身
|
||
var renderers = animator.GetComponentsInChildren<Renderer>();
|
||
// 不具有Renderer的Animator直接执行摧毁
|
||
if (renderers.Length > 0)
|
||
{
|
||
moveChildren = false;
|
||
parentName = itemRootAnimator;
|
||
}
|
||
}
|
||
}
|
||
|
||
if (parentName == null)
|
||
parentName = CheckComponent<ParticleSystem>(root, itemRootParticle);
|
||
|
||
if (parentName == null)
|
||
{
|
||
var renderer = root.GetComponent<Renderer>();
|
||
if (renderer != null && renderer.enabled && renderer.gameObject.activeInHierarchy &&
|
||
!root.HasComponent<NavMeshObject>())
|
||
parentName = itemRootSceneObj;
|
||
}
|
||
|
||
if (parentName == null)
|
||
parentName = CheckComponent<Collider>(root, itemRootCollider);
|
||
|
||
if (parentName == null)
|
||
parentName = CheckComponent<MovieEventRecv>(root, itemRootMisc);
|
||
|
||
if (parentName == null)
|
||
{
|
||
parentName = CheckComponent<DailyCopySceneAnimCtr>(root, itemRootMisc);
|
||
if (!string.IsNullOrEmpty(parentName))
|
||
moveChildren = false;
|
||
}
|
||
|
||
List<Transform> children = null;
|
||
if (moveChildren)
|
||
{
|
||
children = new List<Transform>();
|
||
var childCount = root.childCount;
|
||
for (var i = childCount - 1; i >= 0; i--)
|
||
{
|
||
var child = root.GetChild(i);
|
||
children.Add(child);
|
||
if (parentName != null)
|
||
{
|
||
RemovePrefab(child.gameObject);
|
||
child.SetParent(null);
|
||
}
|
||
}
|
||
}
|
||
|
||
if (parentName != null)
|
||
{
|
||
RemovePrefab(root.gameObject);
|
||
if (string.IsNullOrEmpty(parentName))
|
||
{
|
||
root.SetParent(null);
|
||
SceneManager.MoveGameObjectToScene(root.gameObject, _newScene);
|
||
}
|
||
else
|
||
{
|
||
var parentObj = _parentGameObjects.Find(a => a.name == parentName);
|
||
if (parentObj == null)
|
||
{
|
||
parentObj = new GameObject(parentName);
|
||
if (parentObj.scene != _newScene)
|
||
SceneManager.MoveGameObjectToScene(parentObj, _newScene);
|
||
_parentGameObjects.Add(parentObj);
|
||
}
|
||
|
||
root.SetParent(null);
|
||
SceneManager.MoveGameObjectToScene(root.gameObject, _newScene);
|
||
root.SetParent(parentObj.transform);
|
||
}
|
||
}
|
||
|
||
if (children != null)
|
||
foreach (var child in children)
|
||
MoveGameObjectToNewScene(child);
|
||
}
|
||
|
||
private static void DestroyComponentByCondition<T>(Predicate<T> condition) where T : Component
|
||
{
|
||
var components = Object.FindObjectsOfType<T>();
|
||
for (var i = 0; i < components.Length; i++)
|
||
if (condition.Invoke(components[i]))
|
||
Object.DestroyImmediate(components[i]);
|
||
}
|
||
// Didn't remove the interface to make grammar check possible
|
||
private static void RemovePrefab(GameObject item)
|
||
{
|
||
#if UNITY_2018_1_OR_NEWER
|
||
var prefabRoot = PrefabUtility.GetNearestPrefabInstanceRoot(item);
|
||
if (prefabRoot != null)
|
||
PrefabUtility.UnpackPrefabInstance(prefabRoot, PrefabUnpackMode.Completely, InteractionMode.AutomatedAction);
|
||
#endif
|
||
}
|
||
|
||
#region 分解场景物体流程
|
||
private void RemoveAreaAssets()
|
||
{
|
||
var areaPath = sceneDisassembledPath.Open(_oldScene.name);
|
||
var removeAssets = (from assetPath in AssetDatabase.GetAllAssetPaths()
|
||
where assetPath.StartsWith(areaPath + "/")
|
||
select assetPath).ToArray();
|
||
for (var i = 0; i < removeAssets.Length; i++)
|
||
{
|
||
EditorUtility.DisplayProgressBar(title, string.Format("Delete Scene Asset: {0} / {1}", i, removeAssets.Length), (float)i / removeAssets.Length);
|
||
AssetDatabase.DeleteAsset(removeAssets[i]);
|
||
}
|
||
}
|
||
|
||
// private void CreateAreas()
|
||
// {
|
||
// var permanentLayer = LayerMask.NameToLayer("T4MLayer");
|
||
// // 切割场景区域块 - 因为需要维护分解和非分解两个流程,暂时不合并两次Transform移动
|
||
// var particleRoot = GameObject.Find(itemRootParticle);
|
||
// if (particleRoot != null)
|
||
// CreateForOneRoot(particleRoot.transform, permanentLayer);
|
||
// var meshRoot = GameObject.Find(itemRootSceneObj);
|
||
// if (meshRoot != null)
|
||
// CreateForOneRoot(meshRoot.transform, permanentLayer);
|
||
// var animRoot = GameObject.Find(itemRootAnimator);
|
||
// if (animRoot != null)
|
||
// CreateForOneRoot(animRoot.transform, permanentLayer);
|
||
// var sceneAssembly = Object.FindObjectOfType<SceneAssembly>();
|
||
// GameObject sceneAssemblyObj;
|
||
// if (sceneAssembly != null)
|
||
// {
|
||
// sceneAssemblyObj = sceneAssembly.gameObject;
|
||
// Object.DestroyImmediate(sceneAssembly);
|
||
// }
|
||
// else
|
||
// sceneAssemblyObj = new GameObject("Scene Assembly");
|
||
// sceneAssembly = sceneAssemblyObj.AddComponent<SceneAssembly>();
|
||
// sceneAssembly.sceneObjs = new List<SceneObjData>();
|
||
// sceneAssembly.sceneMeshes = new List<SceneMeshData>();
|
||
// var rootItems = _oldScene.GetRootGameObjects();
|
||
// var areaPath = sceneDisassembledPath.Open(_oldScene.name);
|
||
// if (!AssetDatabase.IsValidFolder(areaPath))
|
||
// AssetDatabase.CreateFolder(sceneDisassembledPath, _oldScene.name);
|
||
// AssetDatabase.Refresh();
|
||
// GameObject indieRoot = null;
|
||
// var areaRoots = new List<GameObject>();
|
||
// for (var i = 0; i < rootItems.Length; i++)
|
||
// {
|
||
// var rootItem = rootItems[i];
|
||
// if (rootItem.name == itemRootIndie)
|
||
// indieRoot = rootItem;
|
||
// else if (rootItem.name.StartsWith("Area_"))
|
||
// areaRoots.Add(rootItem);
|
||
// else
|
||
// SetStaticUntilAnimator(rootItem.transform);
|
||
// }
|
||
// if (indieRoot != null)
|
||
// {
|
||
// var index = 0;
|
||
// var total = indieRoot.transform.childCount;
|
||
// foreach (Transform child in indieRoot.transform)
|
||
// {
|
||
// EditorUtility.DisplayProgressBar(title, string.Format("Create Indie Item: {0} / {1}", index, total), (float)index / total);
|
||
// child.name = string.Format(itemIndieFormat, index);
|
||
// var meshRenderer = child.GetComponent<Renderer>();
|
||
// var bounds = meshRenderer.bounds;
|
||
// var indie = new SceneMeshData
|
||
// {
|
||
// bundlePath = GetAreaBundleName(_oldScene.name, child.name),
|
||
// assetName = child.name,
|
||
// localPosition = child.localPosition,
|
||
// localRotation = child.localRotation,
|
||
// localScale = child.localScale,
|
||
// lightmapIndex = meshRenderer.lightmapIndex,
|
||
// lightmapScaleOffset = meshRenderer.lightmapScaleOffset,
|
||
// bounds = new Circle { center = bounds.center.RemoveY(), radius = bounds.extents.RemoveY().magnitude }
|
||
// };
|
||
// sceneAssembly.sceneMeshes.Add(indie);
|
||
// PrefabUtility.CreatePrefab(areaPath.Open(child.gameObject.name + ".prefab"), child.gameObject);
|
||
// index++;
|
||
// }
|
||
// Object.DestroyImmediate(indieRoot);
|
||
// }
|
||
// for (var i = 0; i < areaRoots.Count; i++)
|
||
// {
|
||
// var rootItem = areaRoots[i];
|
||
// EditorUtility.DisplayProgressBar(title, string.Format("Create Area: {0} / {1}", i, areaRoots.Count), (float)i / areaRoots.Count);
|
||
// var bounds = new Bounds();
|
||
// GetAreaBounds(ref bounds, rootItem.transform);
|
||
// var area = new SceneObjData
|
||
// {
|
||
// bundlePath = GetAreaBundleName(_oldScene.name, rootItem.name),
|
||
// assetName = rootItem.name,
|
||
// localPosition = rootItem.transform.localPosition,
|
||
// localRotation = rootItem.transform.localRotation,
|
||
// localScale = rootItem.transform.localScale,
|
||
// bounds = new Circle { center = bounds.center.RemoveY(), radius = bounds.extents.RemoveY().magnitude }
|
||
// };
|
||
// if (area.bounds.radius > _itemAreaLength)
|
||
// Debug.LogWarning("Scene " + _oldScene.name + " " + rootItem.name + " has a radius of " + area.bounds.radius);
|
||
// sceneAssembly.sceneObjs.Add(area);
|
||
// rootItem.SetStaticRecursively(false);
|
||
// var areaComponent = rootItem.AddComponent<SceneArea>();
|
||
// areaComponent.InitAreaData();
|
||
// PrefabUtility.CreatePrefab(areaPath.Open(rootItem.name + ".prefab"), rootItem);
|
||
// Object.DestroyImmediate(rootItem);
|
||
// }
|
||
// }
|
||
|
||
// private static void SetStaticUntilAnimator(Transform transform)
|
||
// {
|
||
// var animator = transform.GetComponent<Animator>();
|
||
// if (animator == null)
|
||
// {
|
||
// transform.gameObject.isStatic = transform.GetComponent<ParticleSystem>() == null;
|
||
// foreach (Transform child in transform)
|
||
// SetStaticUntilAnimator(child);
|
||
// }
|
||
// }
|
||
|
||
// private void CreateForOneRoot(Transform root, int permanentLayer)
|
||
// {
|
||
// var childCount = root.childCount;
|
||
// for (var i = childCount - 1; i >= 0; i--)
|
||
// {
|
||
// var child = root.GetChild(i);
|
||
// if (child.gameObject.layer == permanentLayer)
|
||
// MoveAreaItemToParent(child, itemRootPermanent);
|
||
// else
|
||
// {
|
||
// var bounds = new Bounds();
|
||
// GetAreaBounds(ref bounds, child);
|
||
// CreateOneAreaItem(child, bounds);
|
||
// }
|
||
// }
|
||
// if (root.childCount <= 0)
|
||
// Object.DestroyImmediate(root.gameObject);
|
||
// }
|
||
|
||
// public static string GetAreaBundleName(string sceneName, string areaName)
|
||
// {
|
||
// sceneName = Path.GetFileNameWithoutExtension(sceneName);
|
||
// areaName = Path.GetFileNameWithoutExtension(areaName);
|
||
// return string.Format(sceneAreaBundleFormat, sceneName, areaName).ToLower();
|
||
// }
|
||
//
|
||
// private void CreateOneAreaItem(Transform item, Bounds bounds)
|
||
// {
|
||
// // 检测物体尺寸是否应该作为独立物体
|
||
// var rootName = bounds.size.x > _indieItemLength || bounds.size.y > _indieItemLength ||
|
||
// bounds.size.z > _indieItemLength
|
||
// ? itemRootIndie
|
||
// : PositionToAreaName(bounds.center);
|
||
// MoveAreaItemToParent(item, rootName);
|
||
// }
|
||
|
||
// private void MoveAreaItemToParent(Transform item, string rootName)
|
||
// {
|
||
// var rootObj = GameObject.Find(rootName);
|
||
// if (rootObj == null)
|
||
// rootObj = new GameObject(rootName);
|
||
// item.SetParent(rootObj.transform);
|
||
// }
|
||
//
|
||
// private string PositionToAreaName(Vector3 position)
|
||
// {
|
||
// var x = Mathf.FloorToInt((position.x - _sceneClass.BasePosX) / _itemAreaLength);
|
||
// var z = Mathf.FloorToInt((position.z - _sceneClass.BasePosZ) / _itemAreaLength);
|
||
// // 防止负号导致超链接无法生成
|
||
// unchecked
|
||
// {
|
||
// return string.Format(itemRootAreaFormat, (uint)x, (uint)z);
|
||
// }
|
||
// }
|
||
//
|
||
// private void GetAreaBounds(ref Bounds bounds, Transform parent)
|
||
// {
|
||
// Bounds? selfBounds = null;
|
||
// var particle = parent.GetComponent<ParticleSystem>();
|
||
// if (particle != null)
|
||
// {
|
||
// // 可以正确获得渲染边界
|
||
// particle.Simulate(1f);
|
||
// var particleRenderer = particle.GetComponent<ParticleSystemRenderer>();
|
||
// selfBounds = particleRenderer.bounds;
|
||
// var main = particle.main;
|
||
// main.playOnAwake = false;
|
||
// }
|
||
// else
|
||
// {
|
||
// var renderer = parent.GetComponent<Renderer>();
|
||
// if (renderer != null)
|
||
// selfBounds = renderer.bounds;
|
||
// }
|
||
//
|
||
// if (selfBounds != null)
|
||
// {
|
||
// if (bounds.size.x <= 0f)
|
||
// bounds = selfBounds.Value;
|
||
// else
|
||
// bounds.Encapsulate(selfBounds.Value);
|
||
// }
|
||
//
|
||
// foreach (Transform child in parent)
|
||
// GetAreaBounds(ref bounds, child);
|
||
// }
|
||
#endregion
|
||
|
||
#region 读取和写入源场景Hash值
|
||
|
||
public const string sceneHashPath = "sceneHash.txt";
|
||
|
||
public static string GetCurrentScenePath(string scenePath)
|
||
{
|
||
var filePath = Application.dataPath.MoveUp().Open(scenePath);
|
||
var result = string.Empty;
|
||
if (File.Exists(filePath))
|
||
{
|
||
var md5 = MD5.Create();
|
||
byte[] hash;
|
||
using (var fs = File.OpenRead(filePath))
|
||
hash = md5.ComputeHash(fs);
|
||
result = BitConverter.ToString(hash);
|
||
}
|
||
return result;
|
||
}
|
||
|
||
public static string GetLastSceneHash(string scenePath)
|
||
{
|
||
var hashList = GetHashRecord();
|
||
var sceneName = Path.GetFileNameWithoutExtension(scenePath);
|
||
var hash = string.Empty;
|
||
for (var i = 0; i < hashList.Count; i++)
|
||
{
|
||
if (hashList[i].sceneName == sceneName)
|
||
{
|
||
hash = hashList[i].sceneHash;
|
||
break;
|
||
}
|
||
}
|
||
return hash;
|
||
}
|
||
|
||
public static void WriteSceneHash(string scenePath)
|
||
{
|
||
var hashList = GetHashRecord();
|
||
var sceneName = Path.GetFileNameWithoutExtension(scenePath);
|
||
var sceneHash = GetCurrentScenePath(scenePath);
|
||
var add = true;
|
||
for (var i = 0; i < hashList.Count; i++)
|
||
{
|
||
if (hashList[i].sceneName == sceneName)
|
||
{
|
||
sceneHash = hashList[i].sceneHash = sceneHash;
|
||
add = false;
|
||
break;
|
||
}
|
||
}
|
||
if (add)
|
||
hashList.Add(new SceneHashRecord(sceneName, sceneHash));
|
||
var builder = new StringBuilder();
|
||
for (var i = 0; i < hashList.Count; i++)
|
||
hashList[i].AppendLine(builder);
|
||
var filePath = Application.dataPath.MoveUp().Open(sceneDisassembledPath).Open(sceneHashPath);
|
||
var diretory = Path.GetDirectoryName(filePath);
|
||
if (!Directory.Exists(diretory))
|
||
// ReSharper disable once AssignNullToNotNullAttribute
|
||
Directory.CreateDirectory(diretory);
|
||
File.WriteAllText(filePath, builder.ToString());
|
||
}
|
||
|
||
private static List<SceneHashRecord> GetHashRecord()
|
||
{
|
||
var hashList = new List<SceneHashRecord>();
|
||
var filePath = Application.dataPath.MoveUp().Open(sceneDisassembledPath).Open(sceneHashPath);
|
||
if (File.Exists(filePath))
|
||
{
|
||
var lines = File.ReadAllLines(filePath);
|
||
for (var i = 0; i < lines.Length; i++)
|
||
{
|
||
var hash = SceneHashRecord.CreateFromLine(lines[i]);
|
||
if (hash != null)
|
||
hashList.Add(hash);
|
||
}
|
||
}
|
||
return hashList;
|
||
}
|
||
#endregion
|
||
|
||
private enum SceneDisassemblyProcess
|
||
{
|
||
Open,
|
||
Create,
|
||
Disassemble,
|
||
CreateLightData,
|
||
CreateAreas,
|
||
Save,
|
||
End
|
||
}
|
||
|
||
private class SceneHashRecord
|
||
{
|
||
public string sceneName;
|
||
public string sceneHash;
|
||
|
||
public static SceneHashRecord CreateFromLine(string line)
|
||
{
|
||
SceneHashRecord result = null;
|
||
if (!string.IsNullOrEmpty(line))
|
||
{
|
||
var segments = line.Split('\t');
|
||
if (segments.Length == 2)
|
||
result = new SceneHashRecord(segments[0], segments[1]);
|
||
}
|
||
return result;
|
||
}
|
||
|
||
public void AppendLine(StringBuilder builder)
|
||
{
|
||
builder.AppendLine(sceneName + "\t" + sceneHash);
|
||
}
|
||
|
||
public SceneHashRecord(string sceneName, string sceneHash)
|
||
{
|
||
this.sceneName = sceneName;
|
||
this.sceneHash = sceneHash;
|
||
}
|
||
}
|
||
}
|
||
|
||
public class DisassembleAllScenesAction
|
||
{
|
||
private readonly List<string> _sceneList;
|
||
private int _sceneIndex;
|
||
private SceneDisassembly _current;
|
||
|
||
public DisassembleAllScenesAction(List<string> sceneList)
|
||
{
|
||
_sceneList = sceneList;
|
||
}
|
||
|
||
public event UnityAction<bool, DisassembleAllScenesAction> onFinish;
|
||
|
||
public void Start()
|
||
{
|
||
_sceneIndex = -1;
|
||
// 2018.12.11 - RoleSelect不复制,而是直接使用源文件。之前流程会导致RoleSelect系列场景在Bundle中存在两份。
|
||
// 预先处理RoleSelect系列的场景
|
||
// 预先处理RoleSelect场景
|
||
//var roleSelectSubPaths = (from assetPath in AssetDatabase.GetAllAssetPaths()
|
||
// where assetPath.StartsWith(SceneDisassembly.roleSelectDir + "/", StringComparison.OrdinalIgnoreCase)
|
||
// where Path.GetExtension(assetPath) == SceneDisassembly.sceneExtension
|
||
// select assetPath).ToArray();
|
||
//for (var i = 0; i < roleSelectSubPaths.Length; i++)
|
||
//{
|
||
// var assetPath = roleSelectSubPaths[i];
|
||
// var scenePath = SceneDisassembly.roleSelectTargetDir.Open(Path.GetFileName(assetPath));
|
||
// var lastHash = SceneDisassembly.GetLastSceneHash(assetPath);
|
||
// var currentHash = SceneDisassembly.GetCurrentScenePath(assetPath);
|
||
// if (lastHash != currentHash)
|
||
// {
|
||
// var roleSelect = EditorSceneManager.OpenScene(assetPath);
|
||
// BuiltinAssetBuilder.ReplaceForOneScene(roleSelect);
|
||
// EditorSceneManager.SaveScene(roleSelect, scenePath);
|
||
// SceneDisassembly.WriteSceneHash(assetPath);
|
||
// }
|
||
// else
|
||
// Debug.LogWarning("选角场景 " + assetPath + " Hash匹配,跳过复制流程!");
|
||
//}
|
||
OnCurrentFinish(true, null);
|
||
}
|
||
|
||
private void OnCurrentFinish(bool success, EditorPerFrameActionBase action)
|
||
{
|
||
var finish = false;
|
||
if (success)
|
||
{
|
||
_sceneIndex++;
|
||
if (_sceneIndex < _sceneList.Count)
|
||
{
|
||
var scenePath = SceneDisassembly.sceneRoot.Open(_sceneList[_sceneIndex]);
|
||
_current = new SceneDisassembly(scenePath);
|
||
_current.onFinish += OnCurrentFinish;
|
||
_current.Start();
|
||
Debug.LogWarning("Start Scene " + _sceneIndex + " to " + scenePath);
|
||
}
|
||
else
|
||
{
|
||
finish = true;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
finish = true;
|
||
}
|
||
|
||
if (finish)
|
||
{
|
||
_current = null;
|
||
if (success)
|
||
{
|
||
AssetDatabase.SaveAssets();
|
||
AssetDatabase.Refresh();
|
||
Debug.LogWarning("All Scene Dissemble Finish");
|
||
}
|
||
else
|
||
Debug.LogError("Error in Scene Dissemble");
|
||
if (onFinish != null)
|
||
onFinish(success, this);
|
||
}
|
||
}
|
||
} |