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);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|